xsave.c 8.0 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * xsave/xrstor support.
 *
 * Author: Suresh Siddha <suresh.b.siddha@intel.com>
 */
#include <linux/bootmem.h>
#include <linux/compat.h>
#include <asm/i387.h>
9 10 11
#ifdef CONFIG_IA32_EMULATION
#include <asm/sigcontext32.h>
#endif
12
#include <asm/xcr.h>
13 14 15 16

/*
 * Supported feature mask by the CPU and the kernel.
 */
17
u64 pcntxt_mask;
18

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
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;
}

72 73 74 75 76 77 78 79 80 81 82 83 84
#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;

85
	BUG_ON(sig_xstate_size < xstate_size);
86

87
	if ((unsigned long)buf % 64)
88 89 90 91 92 93
		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) {
94 95 96 97
		/*
	 	 * Start with clearing the user buffer. This will present a
	 	 * clean context for the bytes not touched by the fxsave/xsave.
		 */
98 99 100
		err = __clear_user(buf, sig_xstate_size);
		if (err)
			return err;
101

102 103 104 105 106
		if (task_thread_info(tsk)->status & TS_XSAVE)
			err = xsave_user(buf);
		else
			err = fxsave_user(buf);

107 108 109 110 111 112 113 114 115
		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;
	}
116 117 118

	if (task_thread_info(tsk)->status & TS_XSAVE) {
		struct _fpstate __user *fx = buf;
119 120
		struct _xstate __user *x = buf;
		u64 xstate_bv;
121 122 123 124 125 126 127

		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));
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

		/*
		 * Read the xstate_bv which we copied (directly from the cpu or
		 * from the state in task struct) to the user buffers and
		 * set the FP/SSE bits.
		 */
		err |= __get_user(xstate_bv, &x->xstate_hdr.xstate_bv);

		/*
		 * For legacy compatible, we always set FP/SSE bits in the bit
		 * vector while saving the state to the user context. This will
		 * enable us capturing any changes(during sigreturn) to
		 * the FP/SSE bits by the legacy applications which don't touch
		 * xstate_bv in the xsave header.
		 *
		 * xsave aware apps can change the xstate_bv in the xsave
		 * header as well as change any contents in the memory layout.
		 * xrestore as part of sigreturn will capture all the changes.
		 */
		xstate_bv |= XSTATE_FPSSE;

		err |= __put_user(xstate_bv, &x->xstate_hdr.xstate_bv);

151 152
		if (err)
			return err;
153 154
	}

155 156 157
	return 1;
}

158 159 160 161
/*
 * Restore the extended state if present. Otherwise, restore the FP/SSE
 * state.
 */
162
static int restore_user_xstate(void __user *buf)
163 164
{
	struct _fpx_sw_bytes fx_sw_user;
165
	u64 mask;
166 167 168 169 170 171
	int err;

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

172
	mask = fx_sw_user.xstate_bv;
173 174 175 176

	/*
	 * restore the state passed by the user.
	 */
177
	err = xrestore_user(buf, mask);
178 179 180 181 182 183
	if (err)
		return err;

	/*
	 * init the state skipped by the user.
	 */
184
	mask = pcntxt_mask & ~mask;
185

186
	xrstor_state(init_xstate_buf, mask);
187 188 189 190 191 192 193 194 195

	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.
	 */
196
	xrstor_state(init_xstate_buf, pcntxt_mask & ~XSTATE_FPSSE);
197 198 199
	return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
}

200 201 202 203 204 205
/*
 * This restores directly out of user space. Exceptions are handled.
 */
int restore_i387_xstate(void __user *buf)
{
	struct task_struct *tsk = current;
206
	int err = 0;
207 208

	if (!buf) {
209 210
		if (used_math())
			goto clear;
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
		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;
	}
226 227 228 229 230
	if (task_thread_info(tsk)->status & TS_XSAVE)
		err = restore_user_xstate(buf);
	else
		err = fxrstor_checking((__force struct i387_fxsave_struct *)
				       buf);
231 232 233 234 235
	if (unlikely(err)) {
		/*
		 * Encountered an error while doing the restore from the
		 * user buffer, clear the fpu state.
		 */
236
clear:
237 238 239 240 241 242 243
		clear_fpu(tsk);
		clear_used_math();
	}
	return err;
}
#endif

244 245 246 247 248 249 250
/*
 * 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.
 */
R
roel kluin 已提交
251
static void prepare_fx_sw_frame(void)
252 253 254 255 256 257 258 259 260 261 262 263 264 265
{
	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;
266
	fx_sw_reserved.xstate_bv = pcntxt_mask;
267 268 269 270 271 272 273 274
	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
}

275 276 277 278 279
/*
 * Represents init state for the supported extended state.
 */
struct xsave_struct *init_xstate_buf;

280 281 282 283
#ifdef CONFIG_X86_64
unsigned int sig_xstate_size = sizeof(struct _fpstate);
#endif

284 285 286 287 288 289 290 291 292 293 294 295 296 297
/*
 * Enable the extended processor state save/restore feature
 */
void __cpuinit xsave_init(void)
{
	if (!cpu_has_xsave)
		return;

	set_in_cr4(X86_CR4_OSXSAVE);

	/*
	 * Enable all the features that the HW is capable of
	 * and the Linux kernel is aware of.
	 */
298
	xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
299 300 301 302 303
}

/*
 * setup the xstate image representing the init state
 */
304
static void __init setup_xstate_init(void)
305 306 307 308 309 310 311 312
{
	init_xstate_buf = alloc_bootmem(xstate_size);
	init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT;
}

/*
 * Enable and initialize the xsave feature.
 */
313
void __ref xsave_cntxt_init(void)
314 315 316 317
{
	unsigned int eax, ebx, ecx, edx;

	cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
318
	pcntxt_mask = eax + ((u64)edx << 32);
319

320 321 322
	if ((pcntxt_mask & XSTATE_FPSSE) != XSTATE_FPSSE) {
		printk(KERN_ERR "FP/SSE not shown under xsave features 0x%llx\n",
		       pcntxt_mask);
323 324 325 326 327 328
		BUG();
	}

	/*
	 * for now OS knows only about FP/SSE
	 */
329
	pcntxt_mask = pcntxt_mask & XCNTXT_MASK;
330 331 332 333 334 335 336 337
	xsave_init();

	/*
	 * Recompute the context size for enabled features
	 */
	cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
	xstate_size = ebx;

338 339
	prepare_fx_sw_frame();

340 341
	setup_xstate_init();

342
	printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%llx, "
343
	       "cntxt size 0x%x\n",
344
	       pcntxt_mask, xstate_size);
345
}