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

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

x86, xsave: save/restore the extended state context in sigframe



On cpu's supporting xsave/xrstor, fpstate pointer in the sigcontext, will
include the extended state information along with fpstate information. Presence
of extended state information is indicated by the presence
of FP_XSTATE_MAGIC1 at fpstate.sw_reserved.magic1 and FP_XSTATE_MAGIC2
at fpstate + (fpstate.sw_reserved.extended_size - FP_XSTATE_MAGIC2_SIZE).

Extended feature bit mask that is saved in the memory layout is represented
by the fpstate.sw_reserved.xstate_bv

For RT signal frames, UC_FP_XSTATE in the uc_flags also indicate the
presence of extended state information in the sigcontext's fpstate
pointer.

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 bdd8caba
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -544,6 +544,9 @@ 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);
+76 −6
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
# 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
@@ -447,12 +448,30 @@ 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_xstate_ia32(void __user *buf)
{
	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
@@ -477,6 +496,8 @@ int save_i387_xstate_ia32(void __user *buf)

	unlazy_fpu(tsk);

	if (cpu_has_xsave)
		return save_i387_xsave(fp);
	if (cpu_has_fxsr)
		return save_i387_fxsave(fp);
	else
@@ -491,14 +512,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)))
@@ -508,6 +530,51 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
	return 0;
}

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;
	unsigned int lmask, hmask;
	int err;

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

	lmask = fx_sw_user.xstate_bv;
	hmask = fx_sw_user.xstate_bv >> 32;

	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);

	xsave_hdr->xstate_bv &=  (pcntxt_lmask | (((u64) pcntxt_hmask) << 32));
	/*
	 * 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.
	 */
	lmask = ~(pcntxt_lmask & ~lmask);
	hmask = ~(pcntxt_hmask & ~hmask);
	xsave_hdr->xstate_bv &=  (lmask | (((u64) hmask) << 32));

	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;
@@ -535,8 +602,11 @@ int restore_i387_xstate_ia32(void __user *buf)
	}

	if (HAVE_HWFP) {
		if (cpu_has_fxsr)
			err = restore_i387_fxsave(fp);
		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(fp);
	} else {
+4 −1
Original line number Diff line number Diff line
@@ -441,6 +441,9 @@ static int 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);
+5 −2
Original line number Diff line number Diff line
@@ -192,7 +192,7 @@ get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size)
			sp = current->sas_ss_sp + current->sas_ss_size;
	}

	return (void __user *)round_down(sp - size, 16);
	return (void __user *)round_down(sp - size, 64);
}

static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
@@ -226,6 +226,9 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
	}
		
	/* 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(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+162 −10
Original line number Diff line number Diff line
@@ -6,12 +6,68 @@
#include <linux/bootmem.h>
#include <linux/compat.h>
#include <asm/i387.h>
#ifdef CONFIG_IA32_EMULATION
#include <asm/sigcontext32.h>
#endif

/*
 * Supported feature mask by the CPU and the kernel.
 */
unsigned int pcntxt_hmask, pcntxt_lmask;

struct _fpx_sw_bytes fx_sw_reserved;
#ifdef CONFIG_IA32_EMULATION
struct _fpx_sw_bytes fx_sw_reserved_ia32;
#endif

/*
 * Check for the presence of extended state information in the
 * user fpstate pointer in the sigcontext.
 */
int check_for_xstate(struct i387_fxsave_struct __user *buf,
		     void __user *fpstate,
		     struct _fpx_sw_bytes *fx_sw_user)
{
	int min_xstate_size = sizeof(struct i387_fxsave_struct) +
			      sizeof(struct xsave_hdr_struct);
	unsigned int magic2;
	int err;

	err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0],
			       sizeof(struct _fpx_sw_bytes));

	if (err)
		return err;

	/*
	 * First Magic check failed.
	 */
	if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1)
		return -1;

	/*
	 * Check for error scenarios.
	 */
	if (fx_sw_user->xstate_size < min_xstate_size ||
	    fx_sw_user->xstate_size > xstate_size ||
	    fx_sw_user->xstate_size > fx_sw_user->extended_size)
		return -1;

	err = __get_user(magic2, (__u32 *) (((void *)fpstate) +
					    fx_sw_user->extended_size -
					    FP_XSTATE_MAGIC2_SIZE));
	/*
	 * Check for the presence of second magic word at the end of memory
	 * layout. This detects the case where the user just copied the legacy
	 * fpstate layout with out copying the extended state information
	 * in the memory layout.
	 */
	if (err || magic2 != FP_XSTATE_MAGIC2)
		return -1;

	return 0;
}

#ifdef CONFIG_X86_64
/*
 * Signal frame handlers.
@@ -28,15 +84,18 @@ int save_i387_xstate(void __user *buf)
	BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
			sizeof(tsk->thread.xstate->fxsave));

	if ((unsigned long)buf % 16)
	if ((unsigned long)buf % 64)
		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 (task_thread_info(tsk)->status & TS_XSAVE)
			err = xsave_user(buf);
		else
			err = fxsave_user(buf);

		if (err)
			return err;
		task_thread_info(tsk)->status &= ~TS_USEDFPU;
@@ -46,23 +105,77 @@ int save_i387_xstate(void __user *buf)
				   xstate_size))
			return -1;
	}

	if (task_thread_info(tsk)->status & TS_XSAVE) {
		struct _fpstate __user *fx = buf;

		err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved,
				     sizeof(struct _fpx_sw_bytes));

		err |= __put_user(FP_XSTATE_MAGIC2,
				  (__u32 __user *) (buf + sig_xstate_size
						    - FP_XSTATE_MAGIC2_SIZE));
	}

	return 1;
}

/*
 * Restore the extended state if present. Otherwise, restore the FP/SSE
 * state.
 */
int restore_user_xstate(void __user *buf)
{
	struct _fpx_sw_bytes fx_sw_user;
	unsigned int lmask, hmask;
	int err;

	if (((unsigned long)buf % 64) ||
	     check_for_xstate(buf, buf, &fx_sw_user))
		goto fx_only;

	lmask = fx_sw_user.xstate_bv;
	hmask = fx_sw_user.xstate_bv >> 32;

	/*
	 * restore the state passed by the user.
	 */
	err = xrestore_user(buf, lmask, hmask);
	if (err)
		return err;

	/*
	 * init the state skipped by the user.
	 */
	lmask = pcntxt_lmask & ~lmask;
	hmask = pcntxt_hmask & ~hmask;

	xrstor_state(init_xstate_buf, lmask, hmask);

	return 0;

fx_only:
	/*
	 * couldn't find the extended state information in the
	 * memory layout. Restore just the FP/SSE and init all
	 * the other extended state.
	 */
	xrstor_state(init_xstate_buf, pcntxt_lmask & ~XSTATE_FPSSE,
		     pcntxt_hmask);
	return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
}

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

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

		if (used_math())
			goto clear;
		return 0;
	} else
		if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
@@ -78,12 +191,17 @@ int restore_i387_xstate(void __user *buf)
		clts();
		task_thread_info(current)->status |= TS_USEDFPU;
	}
	err = fxrstor_checking((__force struct i387_fxsave_struct *)buf);
	if (task_thread_info(tsk)->status & TS_XSAVE)
		err = restore_user_xstate(buf);
	else
		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:
		clear_fpu(tsk);
		clear_used_math();
	}
@@ -91,6 +209,38 @@ int restore_i387_xstate(void __user *buf)
}
#endif

/*
 * Prepare the SW reserved portion of the fxsave memory layout, indicating
 * the presence of the extended state information in the memory layout
 * pointed by the fpstate pointer in the sigcontext.
 * This will be saved when ever the FP and extended state context is
 * saved on the user stack during the signal handler delivery to the user.
 */
void prepare_fx_sw_frame(void)
{
	int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) +
			     FP_XSTATE_MAGIC2_SIZE;

	sig_xstate_size = sizeof(struct _fpstate) + size_extended;

#ifdef CONFIG_IA32_EMULATION
	sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended;
#endif

	memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved));

	fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
	fx_sw_reserved.extended_size = sig_xstate_size;
	fx_sw_reserved.xstate_bv = pcntxt_lmask |
					 (((u64) (pcntxt_hmask)) << 32);
	fx_sw_reserved.xstate_size = xstate_size;
#ifdef CONFIG_IA32_EMULATION
	memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved,
	       sizeof(struct _fpx_sw_bytes));
	fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size;
#endif
}

/*
 * Represents init state for the supported extended state.
 */
@@ -162,6 +312,8 @@ void __init xsave_cntxt_init(void)

	xstate_size = ebx;

	prepare_fx_sw_frame();

	setup_xstate_init();

	printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%Lx, "
Loading