diff options
Diffstat (limited to 'arch/x86/kernel/xsave.c')
-rw-r--r-- | arch/x86/kernel/xsave.c | 172 |
1 files changed, 162 insertions, 10 deletions
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index 608e72d7ca64..dd66d0714c18 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c | |||
@@ -6,12 +6,68 @@ | |||
6 | #include <linux/bootmem.h> | 6 | #include <linux/bootmem.h> |
7 | #include <linux/compat.h> | 7 | #include <linux/compat.h> |
8 | #include <asm/i387.h> | 8 | #include <asm/i387.h> |
9 | #ifdef CONFIG_IA32_EMULATION | ||
10 | #include <asm/sigcontext32.h> | ||
11 | #endif | ||
9 | 12 | ||
10 | /* | 13 | /* |
11 | * Supported feature mask by the CPU and the kernel. | 14 | * Supported feature mask by the CPU and the kernel. |
12 | */ | 15 | */ |
13 | unsigned int pcntxt_hmask, pcntxt_lmask; | 16 | unsigned int pcntxt_hmask, pcntxt_lmask; |
14 | 17 | ||
18 | struct _fpx_sw_bytes fx_sw_reserved; | ||
19 | #ifdef CONFIG_IA32_EMULATION | ||
20 | struct _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 | */ | ||
27 | int 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 | |||
15 | #ifdef CONFIG_X86_64 | 71 | #ifdef CONFIG_X86_64 |
16 | /* | 72 | /* |
17 | * Signal frame handlers. | 73 | * Signal frame handlers. |
@@ -28,15 +84,18 @@ int save_i387_xstate(void __user *buf) | |||
28 | BUILD_BUG_ON(sizeof(struct user_i387_struct) != | 84 | BUILD_BUG_ON(sizeof(struct user_i387_struct) != |
29 | sizeof(tsk->thread.xstate->fxsave)); | 85 | sizeof(tsk->thread.xstate->fxsave)); |
30 | 86 | ||
31 | if ((unsigned long)buf % 16) | 87 | if ((unsigned long)buf % 64) |
32 | printk("save_i387_xstate: bad fpstate %p\n", buf); | 88 | printk("save_i387_xstate: bad fpstate %p\n", buf); |
33 | 89 | ||
34 | if (!used_math()) | 90 | if (!used_math()) |
35 | return 0; | 91 | return 0; |
36 | clear_used_math(); /* trigger finit */ | 92 | clear_used_math(); /* trigger finit */ |
37 | if (task_thread_info(tsk)->status & TS_USEDFPU) { | 93 | if (task_thread_info(tsk)->status & TS_USEDFPU) { |
38 | err = save_i387_checking((struct i387_fxsave_struct __user *) | 94 | if (task_thread_info(tsk)->status & TS_XSAVE) |
39 | buf); | 95 | err = xsave_user(buf); |
96 | else | ||
97 | err = fxsave_user(buf); | ||
98 | |||
40 | if (err) | 99 | if (err) |
41 | return err; | 100 | return err; |
42 | task_thread_info(tsk)->status &= ~TS_USEDFPU; | 101 | task_thread_info(tsk)->status &= ~TS_USEDFPU; |
@@ -46,23 +105,77 @@ int save_i387_xstate(void __user *buf) | |||
46 | xstate_size)) | 105 | xstate_size)) |
47 | return -1; | 106 | return -1; |
48 | } | 107 | } |
108 | |||
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 | |||
49 | return 1; | 120 | return 1; |
50 | } | 121 | } |
51 | 122 | ||
52 | /* | 123 | /* |
124 | * Restore the extended state if present. Otherwise, restore the FP/SSE | ||
125 | * state. | ||
126 | */ | ||
127 | int 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 | |||
157 | fx_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 | /* | ||
53 | * This restores directly out of user space. Exceptions are handled. | 169 | * This restores directly out of user space. Exceptions are handled. |
54 | */ | 170 | */ |
55 | int restore_i387_xstate(void __user *buf) | 171 | int restore_i387_xstate(void __user *buf) |
56 | { | 172 | { |
57 | struct task_struct *tsk = current; | 173 | struct task_struct *tsk = current; |
58 | int err; | 174 | int err = 0; |
59 | 175 | ||
60 | if (!buf) { | 176 | if (!buf) { |
61 | if (used_math()) { | 177 | if (used_math()) |
62 | clear_fpu(tsk); | 178 | goto clear; |
63 | clear_used_math(); | ||
64 | } | ||
65 | |||
66 | return 0; | 179 | return 0; |
67 | } else | 180 | } else |
68 | if (!access_ok(VERIFY_READ, buf, sig_xstate_size)) | 181 | if (!access_ok(VERIFY_READ, buf, sig_xstate_size)) |
@@ -78,12 +191,17 @@ int restore_i387_xstate(void __user *buf) | |||
78 | clts(); | 191 | clts(); |
79 | task_thread_info(current)->status |= TS_USEDFPU; | 192 | task_thread_info(current)->status |= TS_USEDFPU; |
80 | } | 193 | } |
81 | err = fxrstor_checking((__force struct i387_fxsave_struct *)buf); | 194 | 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); | ||
82 | if (unlikely(err)) { | 199 | if (unlikely(err)) { |
83 | /* | 200 | /* |
84 | * Encountered an error while doing the restore from the | 201 | * Encountered an error while doing the restore from the |
85 | * user buffer, clear the fpu state. | 202 | * user buffer, clear the fpu state. |
86 | */ | 203 | */ |
204 | clear: | ||
87 | clear_fpu(tsk); | 205 | clear_fpu(tsk); |
88 | clear_used_math(); | 206 | clear_used_math(); |
89 | } | 207 | } |
@@ -92,6 +210,38 @@ int restore_i387_xstate(void __user *buf) | |||
92 | #endif | 210 | #endif |
93 | 211 | ||
94 | /* | 212 | /* |
213 | * 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 | */ | ||
219 | void 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 | /* | ||
95 | * Represents init state for the supported extended state. | 245 | * Represents init state for the supported extended state. |
96 | */ | 246 | */ |
97 | struct xsave_struct *init_xstate_buf; | 247 | struct xsave_struct *init_xstate_buf; |
@@ -162,6 +312,8 @@ void __init xsave_cntxt_init(void) | |||
162 | 312 | ||
163 | xstate_size = ebx; | 313 | xstate_size = ebx; |
164 | 314 | ||
315 | prepare_fx_sw_frame(); | ||
316 | |||
165 | setup_xstate_init(); | 317 | setup_xstate_init(); |
166 | 318 | ||
167 | printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%Lx, " | 319 | printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%Lx, " |