| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2 |  *  Copyright (C) 1994 Linus Torvalds | 
 | 3 |  * | 
 | 4 |  *  Pentium III FXSR, SSE support | 
 | 5 |  *  General FPU state handling cleanups | 
 | 6 |  *	Gareth Hughes <gareth@valinux.com>, May 2000 | 
 | 7 |  */ | 
| Alexey Dobriyan | 129f694 | 2005-06-23 00:08:33 -0700 | [diff] [blame] | 8 | #include <linux/module.h> | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 9 | #include <linux/regset.h> | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 10 | #include <linux/sched.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 11 | #include <linux/slab.h> | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 12 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <asm/sigcontext.h> | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 14 | #include <asm/processor.h> | 
 | 15 | #include <asm/math_emu.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include <asm/uaccess.h> | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 17 | #include <asm/ptrace.h> | 
 | 18 | #include <asm/i387.h> | 
| Linus Torvalds | 1361b83 | 2012-02-21 13:19:22 -0800 | [diff] [blame] | 19 | #include <asm/fpu-internal.h> | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 20 | #include <asm/user.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 22 | #ifdef CONFIG_X86_64 | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 23 | # include <asm/sigcontext32.h> | 
 | 24 | # include <asm/user32.h> | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 25 | #else | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 26 | # define save_i387_xstate_ia32		save_i387_xstate | 
 | 27 | # define restore_i387_xstate_ia32	restore_i387_xstate | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 28 | # define _fpstate_ia32		_fpstate | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 29 | # define _xstate_ia32		_xstate | 
| Suresh Siddha | 3c1c7f1 | 2008-07-29 10:29:21 -0700 | [diff] [blame] | 30 | # define sig_xstate_ia32_size   sig_xstate_size | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 31 | # define fx_sw_reserved_ia32	fx_sw_reserved | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 32 | # define user_i387_ia32_struct	user_i387_struct | 
 | 33 | # define user32_fxsr_struct	user_fxsr_struct | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 34 | #endif | 
 | 35 |  | 
| Linus Torvalds | 8546c00 | 2012-02-21 10:25:45 -0800 | [diff] [blame] | 36 | /* | 
 | 37 |  * Were we in an interrupt that interrupted kernel mode? | 
 | 38 |  * | 
 | 39 |  * We can do a kernel_fpu_begin/end() pair *ONLY* if that | 
 | 40 |  * pair does nothing at all: the thread must not have fpu (so | 
 | 41 |  * that we don't try to save the FPU state), and TS must | 
 | 42 |  * be set (so that the clts/stts pair does nothing that is | 
 | 43 |  * visible in the interrupted kernel thread). | 
 | 44 |  */ | 
 | 45 | static inline bool interrupted_kernel_fpu_idle(void) | 
 | 46 | { | 
 | 47 | 	return !__thread_has_fpu(current) && | 
 | 48 | 		(read_cr0() & X86_CR0_TS); | 
 | 49 | } | 
 | 50 |  | 
 | 51 | /* | 
 | 52 |  * Were we in user mode (or vm86 mode) when we were | 
 | 53 |  * interrupted? | 
 | 54 |  * | 
 | 55 |  * Doing kernel_fpu_begin/end() is ok if we are running | 
 | 56 |  * in an interrupt context from user mode - we'll just | 
 | 57 |  * save the FPU state as required. | 
 | 58 |  */ | 
 | 59 | static inline bool interrupted_user_mode(void) | 
 | 60 | { | 
 | 61 | 	struct pt_regs *regs = get_irq_regs(); | 
 | 62 | 	return regs && user_mode_vm(regs); | 
 | 63 | } | 
 | 64 |  | 
 | 65 | /* | 
 | 66 |  * Can we use the FPU in kernel mode with the | 
 | 67 |  * whole "kernel_fpu_begin/end()" sequence? | 
 | 68 |  * | 
 | 69 |  * It's always ok in process context (ie "not interrupt") | 
 | 70 |  * but it is sometimes ok even from an irq. | 
 | 71 |  */ | 
 | 72 | bool irq_fpu_usable(void) | 
 | 73 | { | 
 | 74 | 	return !in_interrupt() || | 
 | 75 | 		interrupted_user_mode() || | 
 | 76 | 		interrupted_kernel_fpu_idle(); | 
 | 77 | } | 
 | 78 | EXPORT_SYMBOL(irq_fpu_usable); | 
 | 79 |  | 
 | 80 | void kernel_fpu_begin(void) | 
 | 81 | { | 
 | 82 | 	struct task_struct *me = current; | 
 | 83 |  | 
 | 84 | 	WARN_ON_ONCE(!irq_fpu_usable()); | 
 | 85 | 	preempt_disable(); | 
 | 86 | 	if (__thread_has_fpu(me)) { | 
 | 87 | 		__save_init_fpu(me); | 
 | 88 | 		__thread_clear_has_fpu(me); | 
 | 89 | 		/* We do 'stts()' in kernel_fpu_end() */ | 
 | 90 | 	} else { | 
 | 91 | 		percpu_write(fpu_owner_task, NULL); | 
 | 92 | 		clts(); | 
 | 93 | 	} | 
 | 94 | } | 
 | 95 | EXPORT_SYMBOL(kernel_fpu_begin); | 
 | 96 |  | 
 | 97 | void kernel_fpu_end(void) | 
 | 98 | { | 
 | 99 | 	stts(); | 
 | 100 | 	preempt_enable(); | 
 | 101 | } | 
 | 102 | EXPORT_SYMBOL(kernel_fpu_end); | 
 | 103 |  | 
 | 104 | void unlazy_fpu(struct task_struct *tsk) | 
 | 105 | { | 
 | 106 | 	preempt_disable(); | 
 | 107 | 	if (__thread_has_fpu(tsk)) { | 
 | 108 | 		__save_init_fpu(tsk); | 
 | 109 | 		__thread_fpu_end(tsk); | 
 | 110 | 	} else | 
 | 111 | 		tsk->fpu_counter = 0; | 
 | 112 | 	preempt_enable(); | 
 | 113 | } | 
 | 114 | EXPORT_SYMBOL(unlazy_fpu); | 
 | 115 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 | #ifdef CONFIG_MATH_EMULATION | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 117 | # define HAVE_HWFP		(boot_cpu_data.hard_math) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | #else | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 119 | # define HAVE_HWFP		1 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | #endif | 
 | 121 |  | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 122 | static unsigned int		mxcsr_feature_mask __read_mostly = 0xffffffffu; | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 123 | unsigned int xstate_size; | 
| Xiaotian Feng | f45755b | 2010-08-13 15:19:11 +0800 | [diff] [blame] | 124 | EXPORT_SYMBOL_GPL(xstate_size); | 
| Suresh Siddha | 3c1c7f1 | 2008-07-29 10:29:21 -0700 | [diff] [blame] | 125 | unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32); | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 126 | static struct i387_fxsave_struct fx_scratch __cpuinitdata; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 |  | 
| Linus Torvalds | 1361b83 | 2012-02-21 13:19:22 -0800 | [diff] [blame] | 128 | static void __cpuinit mxcsr_feature_mask_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 | { | 
 | 130 | 	unsigned long mask = 0; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 131 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | 	clts(); | 
 | 133 | 	if (cpu_has_fxsr) { | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 134 | 		memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct)); | 
 | 135 | 		asm volatile("fxsave %0" : : "m" (fx_scratch)); | 
 | 136 | 		mask = fx_scratch.mxcsr_mask; | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 137 | 		if (mask == 0) | 
 | 138 | 			mask = 0x0000ffbf; | 
 | 139 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 | 	mxcsr_feature_mask &= mask; | 
 | 141 | 	stts(); | 
 | 142 | } | 
 | 143 |  | 
| Robert Richter | 0e49bf6 | 2010-07-21 19:03:52 +0200 | [diff] [blame] | 144 | static void __cpuinit init_thread_xstate(void) | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 145 | { | 
| Robert Richter | 0e49bf6 | 2010-07-21 19:03:52 +0200 | [diff] [blame] | 146 | 	/* | 
 | 147 | 	 * Note that xstate_size might be overwriten later during | 
 | 148 | 	 * xsave_init(). | 
 | 149 | 	 */ | 
 | 150 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 151 | 	if (!HAVE_HWFP) { | 
| Robert Richter | 1f999ab | 2010-07-21 19:03:57 +0200 | [diff] [blame] | 152 | 		/* | 
 | 153 | 		 * Disable xsave as we do not support it if i387 | 
 | 154 | 		 * emulation is enabled. | 
 | 155 | 		 */ | 
 | 156 | 		setup_clear_cpu_cap(X86_FEATURE_XSAVE); | 
 | 157 | 		setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT); | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 158 | 		xstate_size = sizeof(struct i387_soft_struct); | 
 | 159 | 		return; | 
 | 160 | 	} | 
 | 161 |  | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 162 | 	if (cpu_has_fxsr) | 
 | 163 | 		xstate_size = sizeof(struct i387_fxsave_struct); | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 164 | 	else | 
 | 165 | 		xstate_size = sizeof(struct i387_fsave_struct); | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 166 | } | 
 | 167 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 168 | /* | 
 | 169 |  * Called at bootup to set up the initial FPU state that is later cloned | 
 | 170 |  * into all processes. | 
 | 171 |  */ | 
| Robert Richter | 0e49bf6 | 2010-07-21 19:03:52 +0200 | [diff] [blame] | 172 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 173 | void __cpuinit fpu_init(void) | 
 | 174 | { | 
| Brian Gerst | 6ac8bac | 2010-09-03 21:17:09 -0400 | [diff] [blame] | 175 | 	unsigned long cr0; | 
 | 176 | 	unsigned long cr4_mask = 0; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 177 |  | 
| Brian Gerst | 6ac8bac | 2010-09-03 21:17:09 -0400 | [diff] [blame] | 178 | 	if (cpu_has_fxsr) | 
 | 179 | 		cr4_mask |= X86_CR4_OSFXSR; | 
 | 180 | 	if (cpu_has_xmm) | 
 | 181 | 		cr4_mask |= X86_CR4_OSXMMEXCPT; | 
 | 182 | 	if (cr4_mask) | 
 | 183 | 		set_in_cr4(cr4_mask); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 184 |  | 
| Brian Gerst | 6ac8bac | 2010-09-03 21:17:09 -0400 | [diff] [blame] | 185 | 	cr0 = read_cr0(); | 
 | 186 | 	cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */ | 
 | 187 | 	if (!HAVE_HWFP) | 
 | 188 | 		cr0 |= X86_CR0_EM; | 
 | 189 | 	write_cr0(cr0); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 190 |  | 
| Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 191 | 	if (!smp_processor_id()) | 
 | 192 | 		init_thread_xstate(); | 
| Suresh Siddha | dc1e35c | 2008-07-29 10:29:19 -0700 | [diff] [blame] | 193 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 194 | 	mxcsr_feature_mask_init(); | 
 | 195 | 	/* clean state in init */ | 
| Avi Kivity | c9ad488 | 2010-05-06 11:45:45 +0300 | [diff] [blame] | 196 | 	current_thread_info()->status = 0; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 197 | 	clear_used_math(); | 
 | 198 | } | 
| Robert Richter | 0e49bf6 | 2010-07-21 19:03:52 +0200 | [diff] [blame] | 199 |  | 
| Sheng Yang | 5ee481d | 2010-05-17 17:22:23 +0800 | [diff] [blame] | 200 | void fpu_finit(struct fpu *fpu) | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 201 | { | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 202 | 	if (!HAVE_HWFP) { | 
 | 203 | 		finit_soft_fpu(&fpu->state->soft); | 
 | 204 | 		return; | 
 | 205 | 	} | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 206 |  | 
 | 207 | 	if (cpu_has_fxsr) { | 
 | 208 | 		struct i387_fxsave_struct *fx = &fpu->state->fxsave; | 
 | 209 |  | 
 | 210 | 		memset(fx, 0, xstate_size); | 
 | 211 | 		fx->cwd = 0x37f; | 
 | 212 | 		if (cpu_has_xmm) | 
 | 213 | 			fx->mxcsr = MXCSR_DEFAULT; | 
 | 214 | 	} else { | 
 | 215 | 		struct i387_fsave_struct *fp = &fpu->state->fsave; | 
 | 216 | 		memset(fp, 0, xstate_size); | 
 | 217 | 		fp->cwd = 0xffff037fu; | 
 | 218 | 		fp->swd = 0xffff0000u; | 
 | 219 | 		fp->twd = 0xffffffffu; | 
 | 220 | 		fp->fos = 0xffff0000u; | 
 | 221 | 	} | 
 | 222 | } | 
| Sheng Yang | 5ee481d | 2010-05-17 17:22:23 +0800 | [diff] [blame] | 223 | EXPORT_SYMBOL_GPL(fpu_finit); | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 224 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | /* | 
 | 226 |  * The _current_ task is using the FPU for the first time | 
 | 227 |  * so initialize it and set the mxcsr to its default | 
 | 228 |  * value at reset if we support XMM instructions and then | 
| Lucas De Marchi | 0d2eb44 | 2011-03-17 16:24:16 -0300 | [diff] [blame] | 229 |  * remember the current task has used the FPU. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 230 |  */ | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 231 | int init_fpu(struct task_struct *tsk) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | { | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 233 | 	int ret; | 
 | 234 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 235 | 	if (tsk_used_math(tsk)) { | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 236 | 		if (HAVE_HWFP && tsk == current) | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 237 | 			unlazy_fpu(tsk); | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 238 | 		return 0; | 
 | 239 | 	} | 
 | 240 |  | 
 | 241 | 	/* | 
 | 242 | 	 * Memory allocation at the first usage of the FPU and other state. | 
 | 243 | 	 */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 244 | 	ret = fpu_alloc(&tsk->thread.fpu); | 
 | 245 | 	if (ret) | 
 | 246 | 		return ret; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 247 |  | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 248 | 	fpu_finit(&tsk->thread.fpu); | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 249 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | 	set_stopped_child_used_math(tsk); | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 251 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 252 | } | 
| Avi Kivity | e5c3014 | 2011-01-11 12:15:54 +0200 | [diff] [blame] | 253 | EXPORT_SYMBOL_GPL(init_fpu); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 254 |  | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 255 | /* | 
 | 256 |  * The xstateregs_active() routine is the same as the fpregs_active() routine, | 
 | 257 |  * as the "regset->n" for the xstate regset will be updated based on the feature | 
 | 258 |  * capabilites supported by the xsave. | 
 | 259 |  */ | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 260 | int fpregs_active(struct task_struct *target, const struct user_regset *regset) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 262 | 	return tsk_used_math(target) ? regset->n : 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 | } | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 264 |  | 
 | 265 | int xfpregs_active(struct task_struct *target, const struct user_regset *regset) | 
 | 266 | { | 
 | 267 | 	return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0; | 
 | 268 | } | 
 | 269 |  | 
 | 270 | int xfpregs_get(struct task_struct *target, const struct user_regset *regset, | 
 | 271 | 		unsigned int pos, unsigned int count, | 
 | 272 | 		void *kbuf, void __user *ubuf) | 
 | 273 | { | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 274 | 	int ret; | 
 | 275 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 276 | 	if (!cpu_has_fxsr) | 
 | 277 | 		return -ENODEV; | 
 | 278 |  | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 279 | 	ret = init_fpu(target); | 
 | 280 | 	if (ret) | 
 | 281 | 		return ret; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 282 |  | 
| Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 283 | 	sanitize_i387_state(target); | 
 | 284 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 285 | 	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 286 | 				   &target->thread.fpu.state->fxsave, 0, -1); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 287 | } | 
 | 288 |  | 
 | 289 | int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | 
 | 290 | 		unsigned int pos, unsigned int count, | 
 | 291 | 		const void *kbuf, const void __user *ubuf) | 
 | 292 | { | 
 | 293 | 	int ret; | 
 | 294 |  | 
 | 295 | 	if (!cpu_has_fxsr) | 
 | 296 | 		return -ENODEV; | 
 | 297 |  | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 298 | 	ret = init_fpu(target); | 
 | 299 | 	if (ret) | 
 | 300 | 		return ret; | 
 | 301 |  | 
| Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 302 | 	sanitize_i387_state(target); | 
 | 303 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 304 | 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 305 | 				 &target->thread.fpu.state->fxsave, 0, -1); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 306 |  | 
 | 307 | 	/* | 
 | 308 | 	 * mxcsr reserved bits must be masked to zero for security reasons. | 
 | 309 | 	 */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 310 | 	target->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 311 |  | 
| Suresh Siddha | 42deec6 | 2008-07-29 10:29:26 -0700 | [diff] [blame] | 312 | 	/* | 
 | 313 | 	 * update the header bits in the xsave header, indicating the | 
 | 314 | 	 * presence of FP and SSE state. | 
 | 315 | 	 */ | 
 | 316 | 	if (cpu_has_xsave) | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 317 | 		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE; | 
| Suresh Siddha | 42deec6 | 2008-07-29 10:29:26 -0700 | [diff] [blame] | 318 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 319 | 	return ret; | 
 | 320 | } | 
 | 321 |  | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 322 | int xstateregs_get(struct task_struct *target, const struct user_regset *regset, | 
 | 323 | 		unsigned int pos, unsigned int count, | 
 | 324 | 		void *kbuf, void __user *ubuf) | 
 | 325 | { | 
 | 326 | 	int ret; | 
 | 327 |  | 
 | 328 | 	if (!cpu_has_xsave) | 
 | 329 | 		return -ENODEV; | 
 | 330 |  | 
 | 331 | 	ret = init_fpu(target); | 
 | 332 | 	if (ret) | 
 | 333 | 		return ret; | 
 | 334 |  | 
 | 335 | 	/* | 
| Suresh Siddha | ff7fbc7 | 2010-02-22 14:51:33 -0800 | [diff] [blame] | 336 | 	 * Copy the 48bytes defined by the software first into the xstate | 
 | 337 | 	 * memory layout in the thread struct, so that we can copy the entire | 
 | 338 | 	 * xstateregs to the user using one user_regset_copyout(). | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 339 | 	 */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 340 | 	memcpy(&target->thread.fpu.state->fxsave.sw_reserved, | 
| Suresh Siddha | ff7fbc7 | 2010-02-22 14:51:33 -0800 | [diff] [blame] | 341 | 	       xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes)); | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 342 |  | 
 | 343 | 	/* | 
| Suresh Siddha | ff7fbc7 | 2010-02-22 14:51:33 -0800 | [diff] [blame] | 344 | 	 * Copy the xstate memory layout. | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 345 | 	 */ | 
 | 346 | 	ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 347 | 				  &target->thread.fpu.state->xsave, 0, -1); | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 348 | 	return ret; | 
 | 349 | } | 
 | 350 |  | 
 | 351 | int xstateregs_set(struct task_struct *target, const struct user_regset *regset, | 
 | 352 | 		  unsigned int pos, unsigned int count, | 
 | 353 | 		  const void *kbuf, const void __user *ubuf) | 
 | 354 | { | 
 | 355 | 	int ret; | 
 | 356 | 	struct xsave_hdr_struct *xsave_hdr; | 
 | 357 |  | 
 | 358 | 	if (!cpu_has_xsave) | 
 | 359 | 		return -ENODEV; | 
 | 360 |  | 
 | 361 | 	ret = init_fpu(target); | 
 | 362 | 	if (ret) | 
 | 363 | 		return ret; | 
 | 364 |  | 
 | 365 | 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 366 | 				 &target->thread.fpu.state->xsave, 0, -1); | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 367 |  | 
 | 368 | 	/* | 
 | 369 | 	 * mxcsr reserved bits must be masked to zero for security reasons. | 
 | 370 | 	 */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 371 | 	target->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask; | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 372 |  | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 373 | 	xsave_hdr = &target->thread.fpu.state->xsave.xsave_hdr; | 
| Suresh Siddha | 5b3efd5 | 2010-02-11 11:50:59 -0800 | [diff] [blame] | 374 |  | 
 | 375 | 	xsave_hdr->xstate_bv &= pcntxt_mask; | 
 | 376 | 	/* | 
 | 377 | 	 * These bits must be zero. | 
 | 378 | 	 */ | 
 | 379 | 	xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; | 
 | 380 |  | 
 | 381 | 	return ret; | 
 | 382 | } | 
 | 383 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 384 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 385 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 386 | /* | 
 | 387 |  * FPU tag word conversions. | 
 | 388 |  */ | 
 | 389 |  | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 390 | static inline unsigned short twd_i387_to_fxsr(unsigned short twd) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 391 | { | 
 | 392 | 	unsigned int tmp; /* to avoid 16 bit prefixes in the code */ | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 393 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 394 | 	/* Transform each pair of bits into 01 (valid) or 00 (empty) */ | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 395 | 	tmp = ~twd; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 396 | 	tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 397 | 	/* and move the valid bits to the lower byte. */ | 
 | 398 | 	tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ | 
 | 399 | 	tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ | 
 | 400 | 	tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 401 |  | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 402 | 	return tmp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 | } | 
 | 404 |  | 
| Phil Carmody | 497888c | 2011-07-14 15:07:13 +0300 | [diff] [blame] | 405 | #define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16) | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 406 | #define FP_EXP_TAG_VALID	0 | 
 | 407 | #define FP_EXP_TAG_ZERO		1 | 
 | 408 | #define FP_EXP_TAG_SPECIAL	2 | 
 | 409 | #define FP_EXP_TAG_EMPTY	3 | 
 | 410 |  | 
 | 411 | static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 412 | { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 413 | 	struct _fpxreg *st; | 
 | 414 | 	u32 tos = (fxsave->swd >> 11) & 7; | 
 | 415 | 	u32 twd = (unsigned long) fxsave->twd; | 
 | 416 | 	u32 tag; | 
 | 417 | 	u32 ret = 0xffff0000u; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 418 | 	int i; | 
 | 419 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 420 | 	for (i = 0; i < 8; i++, twd >>= 1) { | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 421 | 		if (twd & 0x1) { | 
 | 422 | 			st = FPREG_ADDR(fxsave, (i - tos) & 7); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 423 |  | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 424 | 			switch (st->exponent & 0x7fff) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 425 | 			case 0x7fff: | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 426 | 				tag = FP_EXP_TAG_SPECIAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 | 				break; | 
 | 428 | 			case 0x0000: | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 429 | 				if (!st->significand[0] && | 
 | 430 | 				    !st->significand[1] && | 
 | 431 | 				    !st->significand[2] && | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 432 | 				    !st->significand[3]) | 
 | 433 | 					tag = FP_EXP_TAG_ZERO; | 
 | 434 | 				else | 
 | 435 | 					tag = FP_EXP_TAG_SPECIAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 436 | 				break; | 
 | 437 | 			default: | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 438 | 				if (st->significand[3] & 0x8000) | 
 | 439 | 					tag = FP_EXP_TAG_VALID; | 
 | 440 | 				else | 
 | 441 | 					tag = FP_EXP_TAG_SPECIAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 | 				break; | 
 | 443 | 			} | 
 | 444 | 		} else { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 445 | 			tag = FP_EXP_TAG_EMPTY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 446 | 		} | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 447 | 		ret |= tag << (2 * i); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 | 	} | 
 | 449 | 	return ret; | 
 | 450 | } | 
 | 451 |  | 
 | 452 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 453 |  * FXSR floating point environment conversions. | 
 | 454 |  */ | 
 | 455 |  | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 456 | static void | 
 | 457 | convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 458 | { | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 459 | 	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 460 | 	struct _fpreg *to = (struct _fpreg *) &env->st_space[0]; | 
 | 461 | 	struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | 	int i; | 
 | 463 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 464 | 	env->cwd = fxsave->cwd | 0xffff0000u; | 
 | 465 | 	env->swd = fxsave->swd | 0xffff0000u; | 
 | 466 | 	env->twd = twd_fxsr_to_i387(fxsave); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 467 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 468 | #ifdef CONFIG_X86_64 | 
 | 469 | 	env->fip = fxsave->rip; | 
 | 470 | 	env->foo = fxsave->rdp; | 
| Brian Gerst | 10c11f3 | 2010-09-03 21:17:13 -0400 | [diff] [blame] | 471 | 	/* | 
 | 472 | 	 * should be actually ds/cs at fpu exception time, but | 
 | 473 | 	 * that information is not available in 64bit mode. | 
 | 474 | 	 */ | 
 | 475 | 	env->fcs = task_pt_regs(tsk)->cs; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 476 | 	if (tsk == current) { | 
| Brian Gerst | 10c11f3 | 2010-09-03 21:17:13 -0400 | [diff] [blame] | 477 | 		savesegment(ds, env->fos); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 478 | 	} else { | 
| Brian Gerst | 10c11f3 | 2010-09-03 21:17:13 -0400 | [diff] [blame] | 479 | 		env->fos = tsk->thread.ds; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | 	} | 
| Brian Gerst | 10c11f3 | 2010-09-03 21:17:13 -0400 | [diff] [blame] | 481 | 	env->fos |= 0xffff0000; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 482 | #else | 
 | 483 | 	env->fip = fxsave->fip; | 
| Jan Beulich | 609b529 | 2008-03-05 08:35:14 +0000 | [diff] [blame] | 484 | 	env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16); | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 485 | 	env->foo = fxsave->foo; | 
 | 486 | 	env->fos = fxsave->fos; | 
 | 487 | #endif | 
 | 488 |  | 
 | 489 | 	for (i = 0; i < 8; ++i) | 
 | 490 | 		memcpy(&to[i], &from[i], sizeof(to[0])); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 491 | } | 
 | 492 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 493 | static void convert_to_fxsr(struct task_struct *tsk, | 
 | 494 | 			    const struct user_i387_ia32_struct *env) | 
 | 495 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 496 | { | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 497 | 	struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 498 | 	struct _fpreg *from = (struct _fpreg *) &env->st_space[0]; | 
 | 499 | 	struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 500 | 	int i; | 
 | 501 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 502 | 	fxsave->cwd = env->cwd; | 
 | 503 | 	fxsave->swd = env->swd; | 
 | 504 | 	fxsave->twd = twd_i387_to_fxsr(env->twd); | 
 | 505 | 	fxsave->fop = (u16) ((u32) env->fcs >> 16); | 
 | 506 | #ifdef CONFIG_X86_64 | 
 | 507 | 	fxsave->rip = env->fip; | 
 | 508 | 	fxsave->rdp = env->foo; | 
 | 509 | 	/* cs and ds ignored */ | 
 | 510 | #else | 
 | 511 | 	fxsave->fip = env->fip; | 
 | 512 | 	fxsave->fcs = (env->fcs & 0xffff); | 
 | 513 | 	fxsave->foo = env->foo; | 
 | 514 | 	fxsave->fos = env->fos; | 
 | 515 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 516 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 517 | 	for (i = 0; i < 8; ++i) | 
 | 518 | 		memcpy(&to[i], &from[i], sizeof(from[0])); | 
 | 519 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 520 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 521 | int fpregs_get(struct task_struct *target, const struct user_regset *regset, | 
 | 522 | 	       unsigned int pos, unsigned int count, | 
 | 523 | 	       void *kbuf, void __user *ubuf) | 
 | 524 | { | 
 | 525 | 	struct user_i387_ia32_struct env; | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 526 | 	int ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 |  | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 528 | 	ret = init_fpu(target); | 
 | 529 | 	if (ret) | 
 | 530 | 		return ret; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 531 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 532 | 	if (!HAVE_HWFP) | 
 | 533 | 		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); | 
 | 534 |  | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 535 | 	if (!cpu_has_fxsr) { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 536 | 		return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 537 | 					   &target->thread.fpu.state->fsave, 0, | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 538 | 					   -1); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 539 | 	} | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 540 |  | 
| Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 541 | 	sanitize_i387_state(target); | 
 | 542 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 543 | 	if (kbuf && pos == 0 && count == sizeof(env)) { | 
 | 544 | 		convert_from_fxsr(kbuf, target); | 
 | 545 | 		return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 546 | 	} | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 547 |  | 
 | 548 | 	convert_from_fxsr(&env, target); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 549 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 550 | 	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1); | 
 | 551 | } | 
 | 552 |  | 
 | 553 | int fpregs_set(struct task_struct *target, const struct user_regset *regset, | 
 | 554 | 	       unsigned int pos, unsigned int count, | 
 | 555 | 	       const void *kbuf, const void __user *ubuf) | 
 | 556 | { | 
 | 557 | 	struct user_i387_ia32_struct env; | 
 | 558 | 	int ret; | 
 | 559 |  | 
| Suresh Siddha | aa283f4 | 2008-03-10 15:28:05 -0700 | [diff] [blame] | 560 | 	ret = init_fpu(target); | 
 | 561 | 	if (ret) | 
 | 562 | 		return ret; | 
 | 563 |  | 
| Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 564 | 	sanitize_i387_state(target); | 
 | 565 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 566 | 	if (!HAVE_HWFP) | 
 | 567 | 		return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); | 
 | 568 |  | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 569 | 	if (!cpu_has_fxsr) { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 570 | 		return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 571 | 					  &target->thread.fpu.state->fsave, 0, -1); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 572 | 	} | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 573 |  | 
 | 574 | 	if (pos > 0 || count < sizeof(env)) | 
 | 575 | 		convert_from_fxsr(&env, target); | 
 | 576 |  | 
 | 577 | 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1); | 
 | 578 | 	if (!ret) | 
 | 579 | 		convert_to_fxsr(target, &env); | 
 | 580 |  | 
| Suresh Siddha | 42deec6 | 2008-07-29 10:29:26 -0700 | [diff] [blame] | 581 | 	/* | 
 | 582 | 	 * update the header bit in the xsave header, indicating the | 
 | 583 | 	 * presence of FP. | 
 | 584 | 	 */ | 
 | 585 | 	if (cpu_has_xsave) | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 586 | 		target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FP; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 587 | 	return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 | } | 
 | 589 |  | 
 | 590 | /* | 
 | 591 |  * Signal frame handlers. | 
 | 592 |  */ | 
 | 593 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 594 | static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | { | 
 | 596 | 	struct task_struct *tsk = current; | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 597 | 	struct i387_fsave_struct *fp = &tsk->thread.fpu.state->fsave; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 598 |  | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 599 | 	fp->status = fp->swd; | 
 | 600 | 	if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 601 | 		return -1; | 
 | 602 | 	return 1; | 
 | 603 | } | 
 | 604 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 605 | static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | { | 
 | 607 | 	struct task_struct *tsk = current; | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 608 | 	struct i387_fxsave_struct *fx = &tsk->thread.fpu.state->fxsave; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 609 | 	struct user_i387_ia32_struct env; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 610 | 	int err = 0; | 
 | 611 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 612 | 	convert_from_fxsr(&env, tsk); | 
 | 613 | 	if (__copy_to_user(buf, &env, sizeof(env))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 614 | 		return -1; | 
 | 615 |  | 
| Suresh Siddha | 61c4628 | 2008-03-10 15:28:04 -0700 | [diff] [blame] | 616 | 	err |= __put_user(fx->swd, &buf->status); | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 617 | 	err |= __put_user(X86_FXSR_MAGIC, &buf->magic); | 
 | 618 | 	if (err) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 619 | 		return -1; | 
 | 620 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 621 | 	if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 622 | 		return -1; | 
 | 623 | 	return 1; | 
 | 624 | } | 
 | 625 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 626 | static int save_i387_xsave(void __user *buf) | 
 | 627 | { | 
| Suresh Siddha | 04944b7 | 2008-10-07 14:04:28 -0700 | [diff] [blame] | 628 | 	struct task_struct *tsk = current; | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 629 | 	struct _fpstate_ia32 __user *fx = buf; | 
 | 630 | 	int err = 0; | 
 | 631 |  | 
| Suresh Siddha | 29104e1 | 2010-07-19 16:05:49 -0700 | [diff] [blame] | 632 |  | 
 | 633 | 	sanitize_i387_state(tsk); | 
 | 634 |  | 
| Suresh Siddha | 04944b7 | 2008-10-07 14:04:28 -0700 | [diff] [blame] | 635 | 	/* | 
 | 636 | 	 * For legacy compatible, we always set FP/SSE bits in the bit | 
 | 637 | 	 * vector while saving the state to the user context. | 
 | 638 | 	 * This will enable us capturing any changes(during sigreturn) to | 
 | 639 | 	 * the FP/SSE bits by the legacy applications which don't touch | 
 | 640 | 	 * xstate_bv in the xsave header. | 
 | 641 | 	 * | 
 | 642 | 	 * xsave aware applications can change the xstate_bv in the xsave | 
 | 643 | 	 * header as well as change any contents in the memory layout. | 
 | 644 | 	 * xrestore as part of sigreturn will capture all the changes. | 
 | 645 | 	 */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 646 | 	tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE; | 
| Suresh Siddha | 04944b7 | 2008-10-07 14:04:28 -0700 | [diff] [blame] | 647 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 648 | 	if (save_i387_fxsave(fx) < 0) | 
 | 649 | 		return -1; | 
 | 650 |  | 
 | 651 | 	err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32, | 
 | 652 | 			     sizeof(struct _fpx_sw_bytes)); | 
 | 653 | 	err |= __put_user(FP_XSTATE_MAGIC2, | 
 | 654 | 			  (__u32 __user *) (buf + sig_xstate_ia32_size | 
 | 655 | 					    - FP_XSTATE_MAGIC2_SIZE)); | 
 | 656 | 	if (err) | 
 | 657 | 		return -1; | 
 | 658 |  | 
 | 659 | 	return 1; | 
 | 660 | } | 
 | 661 |  | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 662 | int save_i387_xstate_ia32(void __user *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 663 | { | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 664 | 	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf; | 
 | 665 | 	struct task_struct *tsk = current; | 
 | 666 |  | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 667 | 	if (!used_math()) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 668 | 		return 0; | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 669 |  | 
 | 670 | 	if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size)) | 
 | 671 | 		return -EACCES; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 672 | 	/* | 
 | 673 | 	 * This will cause a "finit" to be triggered by the next | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 | 	 * attempted FPU operation by the 'current' process. | 
 | 675 | 	 */ | 
 | 676 | 	clear_used_math(); | 
 | 677 |  | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 678 | 	if (!HAVE_HWFP) { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 679 | 		return fpregs_soft_get(current, NULL, | 
 | 680 | 				       0, sizeof(struct user_i387_ia32_struct), | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 681 | 				       NULL, fp) ? -1 : 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 682 | 	} | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 683 |  | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 684 | 	unlazy_fpu(tsk); | 
 | 685 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 686 | 	if (cpu_has_xsave) | 
 | 687 | 		return save_i387_xsave(fp); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 688 | 	if (cpu_has_fxsr) | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 689 | 		return save_i387_fxsave(fp); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 690 | 	else | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 691 | 		return save_i387_fsave(fp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 692 | } | 
 | 693 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 694 | static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 695 | { | 
 | 696 | 	struct task_struct *tsk = current; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 697 |  | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 698 | 	return __copy_from_user(&tsk->thread.fpu.state->fsave, buf, | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 699 | 				sizeof(struct i387_fsave_struct)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 700 | } | 
 | 701 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 702 | static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf, | 
 | 703 | 			       unsigned int size) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 705 | 	struct task_struct *tsk = current; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 706 | 	struct user_i387_ia32_struct env; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 707 | 	int err; | 
 | 708 |  | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 709 | 	err = __copy_from_user(&tsk->thread.fpu.state->fxsave, &buf->_fxsr_env[0], | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 710 | 			       size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 711 | 	/* mxcsr reserved bits must be masked to zero for security reasons */ | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 712 | 	tsk->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask; | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 713 | 	if (err || __copy_from_user(&env, buf, sizeof(env))) | 
 | 714 | 		return 1; | 
 | 715 | 	convert_to_fxsr(tsk, &env); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 716 |  | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 717 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 718 | } | 
 | 719 |  | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 720 | static int restore_i387_xsave(void __user *buf) | 
 | 721 | { | 
 | 722 | 	struct _fpx_sw_bytes fx_sw_user; | 
 | 723 | 	struct _fpstate_ia32 __user *fx_user = | 
 | 724 | 			((struct _fpstate_ia32 __user *) buf); | 
 | 725 | 	struct i387_fxsave_struct __user *fx = | 
 | 726 | 		(struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0]; | 
 | 727 | 	struct xsave_hdr_struct *xsave_hdr = | 
| Avi Kivity | 8660328 | 2010-05-06 11:45:46 +0300 | [diff] [blame] | 728 | 				¤t->thread.fpu.state->xsave.xsave_hdr; | 
| H. Peter Anvin | 6152e4b | 2008-07-29 17:23:16 -0700 | [diff] [blame] | 729 | 	u64 mask; | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 730 | 	int err; | 
 | 731 |  | 
 | 732 | 	if (check_for_xstate(fx, buf, &fx_sw_user)) | 
 | 733 | 		goto fx_only; | 
 | 734 |  | 
| H. Peter Anvin | 6152e4b | 2008-07-29 17:23:16 -0700 | [diff] [blame] | 735 | 	mask = fx_sw_user.xstate_bv; | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 736 |  | 
 | 737 | 	err = restore_i387_fxsave(buf, fx_sw_user.xstate_size); | 
 | 738 |  | 
| H. Peter Anvin | 6152e4b | 2008-07-29 17:23:16 -0700 | [diff] [blame] | 739 | 	xsave_hdr->xstate_bv &= pcntxt_mask; | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 740 | 	/* | 
 | 741 | 	 * These bits must be zero. | 
 | 742 | 	 */ | 
 | 743 | 	xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; | 
 | 744 |  | 
 | 745 | 	/* | 
 | 746 | 	 * Init the state that is not present in the memory layout | 
 | 747 | 	 * and enabled by the OS. | 
 | 748 | 	 */ | 
| H. Peter Anvin | 6152e4b | 2008-07-29 17:23:16 -0700 | [diff] [blame] | 749 | 	mask = ~(pcntxt_mask & ~mask); | 
 | 750 | 	xsave_hdr->xstate_bv &= mask; | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 751 |  | 
 | 752 | 	return err; | 
 | 753 | fx_only: | 
 | 754 | 	/* | 
 | 755 | 	 * Couldn't find the extended state information in the memory | 
 | 756 | 	 * layout. Restore the FP/SSE and init the other extended state | 
 | 757 | 	 * enabled by the OS. | 
 | 758 | 	 */ | 
 | 759 | 	xsave_hdr->xstate_bv = XSTATE_FPSSE; | 
 | 760 | 	return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct)); | 
 | 761 | } | 
 | 762 |  | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 763 | int restore_i387_xstate_ia32(void __user *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 764 | { | 
 | 765 | 	int err; | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 766 | 	struct task_struct *tsk = current; | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 767 | 	struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 768 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 769 | 	if (HAVE_HWFP) | 
| Suresh Siddha | fd3c3ed | 2008-05-07 12:09:52 -0700 | [diff] [blame] | 770 | 		clear_fpu(tsk); | 
 | 771 |  | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 772 | 	if (!buf) { | 
 | 773 | 		if (used_math()) { | 
 | 774 | 			clear_fpu(tsk); | 
 | 775 | 			clear_used_math(); | 
 | 776 | 		} | 
 | 777 |  | 
 | 778 | 		return 0; | 
 | 779 | 	} else | 
 | 780 | 		if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size)) | 
 | 781 | 			return -EACCES; | 
 | 782 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 783 | 	if (!used_math()) { | 
 | 784 | 		err = init_fpu(tsk); | 
 | 785 | 		if (err) | 
 | 786 | 			return err; | 
 | 787 | 	} | 
| Suresh Siddha | fd3c3ed | 2008-05-07 12:09:52 -0700 | [diff] [blame] | 788 |  | 
| Suresh Siddha | e8a496a | 2008-05-23 16:26:37 -0700 | [diff] [blame] | 789 | 	if (HAVE_HWFP) { | 
| Suresh Siddha | c37b5ef | 2008-07-29 10:29:25 -0700 | [diff] [blame] | 790 | 		if (cpu_has_xsave) | 
 | 791 | 			err = restore_i387_xsave(buf); | 
 | 792 | 		else if (cpu_has_fxsr) | 
 | 793 | 			err = restore_i387_fxsave(fp, sizeof(struct | 
 | 794 | 							   i387_fxsave_struct)); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 795 | 		else | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 796 | 			err = restore_i387_fsave(fp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 797 | 	} else { | 
| Roland McGrath | 4421011 | 2008-01-30 13:31:50 +0100 | [diff] [blame] | 798 | 		err = fpregs_soft_set(current, NULL, | 
 | 799 | 				      0, sizeof(struct user_i387_ia32_struct), | 
| Suresh Siddha | ab51370 | 2008-07-29 10:29:22 -0700 | [diff] [blame] | 800 | 				      NULL, fp) != 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 801 | 	} | 
 | 802 | 	set_used_math(); | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 803 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 804 | 	return err; | 
 | 805 | } | 
 | 806 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 807 | /* | 
 | 808 |  * FPU state for core dumps. | 
| Roland McGrath | 60b3b9a | 2008-01-30 13:31:55 +0100 | [diff] [blame] | 809 |  * This is only used for a.out dumps now. | 
 | 810 |  * It is declared generically using elf_fpregset_t (which is | 
 | 811 |  * struct user_i387_struct) but is in fact only used for 32-bit | 
 | 812 |  * dumps, so on 64-bit it is really struct user_i387_ia32_struct. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 813 |  */ | 
| Cyrill Gorcunov | 3b095a0 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 814 | int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 815 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 816 | 	struct task_struct *tsk = current; | 
| Ingo Molnar | f668964 | 2008-03-05 15:37:32 +0100 | [diff] [blame] | 817 | 	int fpvalid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 818 |  | 
 | 819 | 	fpvalid = !!used_math(); | 
| Roland McGrath | 60b3b9a | 2008-01-30 13:31:55 +0100 | [diff] [blame] | 820 | 	if (fpvalid) | 
 | 821 | 		fpvalid = !fpregs_get(tsk, NULL, | 
 | 822 | 				      0, sizeof(struct user_i387_ia32_struct), | 
 | 823 | 				      fpu, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 824 |  | 
 | 825 | 	return fpvalid; | 
 | 826 | } | 
| Alexey Dobriyan | 129f694 | 2005-06-23 00:08:33 -0700 | [diff] [blame] | 827 | EXPORT_SYMBOL(dump_fpu); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 828 |  | 
| Roland McGrath | 60b3b9a | 2008-01-30 13:31:55 +0100 | [diff] [blame] | 829 | #endif	/* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ |