blob: dd66d0714c1820a17aae498e14b67aa746cbd65f [file] [log] [blame]
Suresh Siddhadc1e35c2008-07-29 10:29:19 -07001/*
2 * xsave/xrstor support.
3 *
4 * Author: Suresh Siddha <suresh.b.siddha@intel.com>
5 */
6#include <linux/bootmem.h>
7#include <linux/compat.h>
8#include <asm/i387.h>
Suresh Siddhac37b5ef2008-07-29 10:29:25 -07009#ifdef CONFIG_IA32_EMULATION
10#include <asm/sigcontext32.h>
11#endif
Suresh Siddhadc1e35c2008-07-29 10:29:19 -070012
13/*
14 * Supported feature mask by the CPU and the kernel.
15 */
16unsigned int pcntxt_hmask, pcntxt_lmask;
17
Suresh Siddhac37b5ef2008-07-29 10:29:25 -070018struct _fpx_sw_bytes fx_sw_reserved;
19#ifdef CONFIG_IA32_EMULATION
20struct _fpx_sw_bytes fx_sw_reserved_ia32;
21#endif
22
23/*
24 * Check for the presence of extended state information in the
25 * user fpstate pointer in the sigcontext.
26 */
27int check_for_xstate(struct i387_fxsave_struct __user *buf,
28 void __user *fpstate,
29 struct _fpx_sw_bytes *fx_sw_user)
30{
31 int min_xstate_size = sizeof(struct i387_fxsave_struct) +
32 sizeof(struct xsave_hdr_struct);
33 unsigned int magic2;
34 int err;
35
36 err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0],
37 sizeof(struct _fpx_sw_bytes));
38
39 if (err)
40 return err;
41
42 /*
43 * First Magic check failed.
44 */
45 if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1)
46 return -1;
47
48 /*
49 * Check for error scenarios.
50 */
51 if (fx_sw_user->xstate_size < min_xstate_size ||
52 fx_sw_user->xstate_size > xstate_size ||
53 fx_sw_user->xstate_size > fx_sw_user->extended_size)
54 return -1;
55
56 err = __get_user(magic2, (__u32 *) (((void *)fpstate) +
57 fx_sw_user->extended_size -
58 FP_XSTATE_MAGIC2_SIZE));
59 /*
60 * Check for the presence of second magic word at the end of memory
61 * layout. This detects the case where the user just copied the legacy
62 * fpstate layout with out copying the extended state information
63 * in the memory layout.
64 */
65 if (err || magic2 != FP_XSTATE_MAGIC2)
66 return -1;
67
68 return 0;
69}
70
Suresh Siddhaab513702008-07-29 10:29:22 -070071#ifdef CONFIG_X86_64
72/*
73 * Signal frame handlers.
74 */
75
76int save_i387_xstate(void __user *buf)
77{
78 struct task_struct *tsk = current;
79 int err = 0;
80
81 if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size))
82 return -EACCES;
83
84 BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
85 sizeof(tsk->thread.xstate->fxsave));
86
Suresh Siddhac37b5ef2008-07-29 10:29:25 -070087 if ((unsigned long)buf % 64)
Suresh Siddhaab513702008-07-29 10:29:22 -070088 printk("save_i387_xstate: bad fpstate %p\n", buf);
89
90 if (!used_math())
91 return 0;
92 clear_used_math(); /* trigger finit */
93 if (task_thread_info(tsk)->status & TS_USEDFPU) {
Suresh Siddhac37b5ef2008-07-29 10:29:25 -070094 if (task_thread_info(tsk)->status & TS_XSAVE)
95 err = xsave_user(buf);
96 else
97 err = fxsave_user(buf);
98
Suresh Siddhaab513702008-07-29 10:29:22 -070099 if (err)
100 return err;
101 task_thread_info(tsk)->status &= ~TS_USEDFPU;
102 stts();
103 } else {
104 if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
105 xstate_size))
106 return -1;
107 }
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700108
109 if (task_thread_info(tsk)->status & TS_XSAVE) {
110 struct _fpstate __user *fx = buf;
111
112 err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved,
113 sizeof(struct _fpx_sw_bytes));
114
115 err |= __put_user(FP_XSTATE_MAGIC2,
116 (__u32 __user *) (buf + sig_xstate_size
117 - FP_XSTATE_MAGIC2_SIZE));
118 }
119
Suresh Siddhaab513702008-07-29 10:29:22 -0700120 return 1;
121}
122
123/*
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700124 * Restore the extended state if present. Otherwise, restore the FP/SSE
125 * state.
126 */
127int restore_user_xstate(void __user *buf)
128{
129 struct _fpx_sw_bytes fx_sw_user;
130 unsigned int lmask, hmask;
131 int err;
132
133 if (((unsigned long)buf % 64) ||
134 check_for_xstate(buf, buf, &fx_sw_user))
135 goto fx_only;
136
137 lmask = fx_sw_user.xstate_bv;
138 hmask = fx_sw_user.xstate_bv >> 32;
139
140 /*
141 * restore the state passed by the user.
142 */
143 err = xrestore_user(buf, lmask, hmask);
144 if (err)
145 return err;
146
147 /*
148 * init the state skipped by the user.
149 */
150 lmask = pcntxt_lmask & ~lmask;
151 hmask = pcntxt_hmask & ~hmask;
152
153 xrstor_state(init_xstate_buf, lmask, hmask);
154
155 return 0;
156
157fx_only:
158 /*
159 * couldn't find the extended state information in the
160 * memory layout. Restore just the FP/SSE and init all
161 * the other extended state.
162 */
163 xrstor_state(init_xstate_buf, pcntxt_lmask & ~XSTATE_FPSSE,
164 pcntxt_hmask);
165 return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
166}
167
168/*
Suresh Siddhaab513702008-07-29 10:29:22 -0700169 * This restores directly out of user space. Exceptions are handled.
170 */
171int restore_i387_xstate(void __user *buf)
172{
173 struct task_struct *tsk = current;
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700174 int err = 0;
Suresh Siddhaab513702008-07-29 10:29:22 -0700175
176 if (!buf) {
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700177 if (used_math())
178 goto clear;
Suresh Siddhaab513702008-07-29 10:29:22 -0700179 return 0;
180 } else
181 if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
182 return -EACCES;
183
184 if (!used_math()) {
185 err = init_fpu(tsk);
186 if (err)
187 return err;
188 }
189
190 if (!(task_thread_info(current)->status & TS_USEDFPU)) {
191 clts();
192 task_thread_info(current)->status |= TS_USEDFPU;
193 }
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700194 if (task_thread_info(tsk)->status & TS_XSAVE)
195 err = restore_user_xstate(buf);
196 else
197 err = fxrstor_checking((__force struct i387_fxsave_struct *)
198 buf);
Suresh Siddhaab513702008-07-29 10:29:22 -0700199 if (unlikely(err)) {
200 /*
201 * Encountered an error while doing the restore from the
202 * user buffer, clear the fpu state.
203 */
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700204clear:
Suresh Siddhaab513702008-07-29 10:29:22 -0700205 clear_fpu(tsk);
206 clear_used_math();
207 }
208 return err;
209}
210#endif
211
Suresh Siddhadc1e35c2008-07-29 10:29:19 -0700212/*
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700213 * Prepare the SW reserved portion of the fxsave memory layout, indicating
214 * the presence of the extended state information in the memory layout
215 * pointed by the fpstate pointer in the sigcontext.
216 * This will be saved when ever the FP and extended state context is
217 * saved on the user stack during the signal handler delivery to the user.
218 */
219void prepare_fx_sw_frame(void)
220{
221 int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) +
222 FP_XSTATE_MAGIC2_SIZE;
223
224 sig_xstate_size = sizeof(struct _fpstate) + size_extended;
225
226#ifdef CONFIG_IA32_EMULATION
227 sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended;
228#endif
229
230 memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved));
231
232 fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
233 fx_sw_reserved.extended_size = sig_xstate_size;
234 fx_sw_reserved.xstate_bv = pcntxt_lmask |
235 (((u64) (pcntxt_hmask)) << 32);
236 fx_sw_reserved.xstate_size = xstate_size;
237#ifdef CONFIG_IA32_EMULATION
238 memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved,
239 sizeof(struct _fpx_sw_bytes));
240 fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size;
241#endif
242}
243
244/*
Suresh Siddhadc1e35c2008-07-29 10:29:19 -0700245 * Represents init state for the supported extended state.
246 */
247struct xsave_struct *init_xstate_buf;
248
Suresh Siddha3c1c7f12008-07-29 10:29:21 -0700249#ifdef CONFIG_X86_64
250unsigned int sig_xstate_size = sizeof(struct _fpstate);
251#endif
252
Suresh Siddhadc1e35c2008-07-29 10:29:19 -0700253/*
254 * Enable the extended processor state save/restore feature
255 */
256void __cpuinit xsave_init(void)
257{
258 if (!cpu_has_xsave)
259 return;
260
261 set_in_cr4(X86_CR4_OSXSAVE);
262
263 /*
264 * Enable all the features that the HW is capable of
265 * and the Linux kernel is aware of.
266 *
267 * xsetbv();
268 */
269 asm volatile(".byte 0x0f,0x01,0xd1" : : "c" (0),
270 "a" (pcntxt_lmask), "d" (pcntxt_hmask));
271}
272
273/*
274 * setup the xstate image representing the init state
275 */
276void setup_xstate_init(void)
277{
278 init_xstate_buf = alloc_bootmem(xstate_size);
279 init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT;
280}
281
282/*
283 * Enable and initialize the xsave feature.
284 */
285void __init xsave_cntxt_init(void)
286{
287 unsigned int eax, ebx, ecx, edx;
288
289 cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
290
291 pcntxt_lmask = eax;
292 pcntxt_hmask = edx;
293
294 if ((pcntxt_lmask & XSTATE_FPSSE) != XSTATE_FPSSE) {
295 printk(KERN_ERR "FP/SSE not shown under xsave features %x\n",
296 pcntxt_lmask);
297 BUG();
298 }
299
300 /*
301 * for now OS knows only about FP/SSE
302 */
303 pcntxt_lmask = pcntxt_lmask & XCNTXT_LMASK;
304 pcntxt_hmask = pcntxt_hmask & XCNTXT_HMASK;
305
306 xsave_init();
307
308 /*
309 * Recompute the context size for enabled features
310 */
311 cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
312
313 xstate_size = ebx;
314
Suresh Siddhac37b5ef2008-07-29 10:29:25 -0700315 prepare_fx_sw_frame();
316
Suresh Siddhadc1e35c2008-07-29 10:29:19 -0700317 setup_xstate_init();
318
319 printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%Lx, "
320 "cntxt size 0x%x\n",
321 (pcntxt_lmask | ((u64) pcntxt_hmask << 32)), xstate_size);
322}