]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
30 | */ | |
31 | /* | |
32 | * Mach Operating System | |
33 | * Copyright (c) 1992-1990 Carnegie Mellon University | |
34 | * All Rights Reserved. | |
35 | * | |
36 | * Permission to use, copy, modify and distribute this software and its | |
37 | * documentation is hereby granted, provided that both the copyright | |
38 | * notice and this permission notice appear in all copies of the | |
39 | * software, derivative works or modified versions, and any portions | |
40 | * thereof, and that both notices appear in supporting documentation. | |
41 | * | |
42 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
43 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
44 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
45 | * | |
46 | * Carnegie Mellon requests users of this software to return to | |
47 | * | |
48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
49 | * School of Computer Science | |
50 | * Carnegie Mellon University | |
51 | * Pittsburgh PA 15213-3890 | |
52 | * | |
53 | * any improvements or extensions that they make and grant Carnegie Mellon | |
54 | * the rights to redistribute these changes. | |
55 | */ | |
56 | /* | |
57 | */ | |
58 | ||
1c79356b A |
59 | #include <platforms.h> |
60 | ||
61 | #include <mach/exception_types.h> | |
62 | #include <mach/i386/thread_status.h> | |
63 | #include <mach/i386/fp_reg.h> | |
64 | ||
65 | #include <kern/mach_param.h> | |
91447636 | 66 | #include <kern/processor.h> |
1c79356b A |
67 | #include <kern/thread.h> |
68 | #include <kern/zalloc.h> | |
69 | #include <kern/misc_protos.h> | |
70 | #include <kern/spl.h> | |
71 | #include <kern/assert.h> | |
72 | ||
0c530ab8 | 73 | #include <architecture/i386/pio.h> |
55e303ae | 74 | #include <i386/cpuid.h> |
b0d623f7 | 75 | #include <i386/fpu.h> |
0c530ab8 | 76 | #include <i386/proc_reg.h> |
b0d623f7 A |
77 | #include <i386/misc_protos.h> |
78 | #include <i386/thread.h> | |
79 | #include <i386/trap.h> | |
1c79356b | 80 | |
0c530ab8 | 81 | int fp_kind = FP_NO; /* not inited */ |
1c79356b A |
82 | zone_t ifps_zone; /* zone for FPU save area */ |
83 | ||
b0d623f7 | 84 | #define ALIGNED(addr,size) (((uintptr_t)(addr)&((size)-1))==0) |
1c79356b A |
85 | |
86 | /* Forward */ | |
87 | ||
88 | extern void fpinit(void); | |
89 | extern void fp_save( | |
91447636 | 90 | thread_t thr_act); |
1c79356b | 91 | extern void fp_load( |
91447636 | 92 | thread_t thr_act); |
1c79356b | 93 | |
0c530ab8 A |
94 | static void configure_mxcsr_capability_mask(struct x86_fpsave_state *ifps); |
95 | ||
96 | struct x86_fpsave_state starting_fp_state; | |
97 | ||
98 | ||
99 | /* Global MXCSR capability bitmask */ | |
100 | static unsigned int mxcsr_capability_mask; | |
101 | ||
102 | /* | |
103 | * Determine the MXCSR capability mask, which allows us to mask off any | |
104 | * potentially unsafe "reserved" bits before restoring the FPU context. | |
105 | * *Not* per-cpu, assumes symmetry. | |
106 | */ | |
107 | static void | |
108 | configure_mxcsr_capability_mask(struct x86_fpsave_state *ifps) | |
109 | { | |
110 | /* FXSAVE requires a 16 byte aligned store */ | |
111 | assert(ALIGNED(ifps,16)); | |
112 | /* Clear, to prepare for the diagnostic FXSAVE */ | |
113 | bzero(ifps, sizeof(*ifps)); | |
114 | /* Disable FPU/SSE Device Not Available exceptions */ | |
115 | clear_ts(); | |
0c530ab8 A |
116 | __asm__ volatile("fxsave %0" : "=m" (ifps->fx_save_state)); |
117 | mxcsr_capability_mask = ifps->fx_save_state.fx_MXCSR_MASK; | |
118 | ||
119 | /* Set default mask value if necessary */ | |
120 | if (mxcsr_capability_mask == 0) | |
121 | mxcsr_capability_mask = 0xffbf; | |
122 | ||
123 | /* Re-enable FPU/SSE DNA exceptions */ | |
124 | set_ts(); | |
125 | } | |
126 | ||
127 | /* | |
128 | * Allocate and initialize FP state for current thread. | |
129 | * Don't load state. | |
130 | */ | |
131 | static struct x86_fpsave_state * | |
132 | fp_state_alloc(void) | |
133 | { | |
134 | struct x86_fpsave_state *ifps; | |
135 | ||
136 | ifps = (struct x86_fpsave_state *)zalloc(ifps_zone); | |
137 | assert(ALIGNED(ifps,16)); | |
138 | bzero((char *)ifps, sizeof *ifps); | |
139 | ||
140 | return ifps; | |
141 | } | |
142 | ||
143 | static inline void | |
144 | fp_state_free(struct x86_fpsave_state *ifps) | |
145 | { | |
146 | zfree(ifps_zone, ifps); | |
147 | } | |
148 | ||
149 | ||
1c79356b A |
150 | /* |
151 | * Look for FPU and initialize it. | |
152 | * Called on each CPU. | |
153 | */ | |
154 | void | |
155 | init_fpu(void) | |
156 | { | |
157 | unsigned short status, control; | |
158 | ||
159 | /* | |
160 | * Check for FPU by initializing it, | |
161 | * then trying to read the correct bit patterns from | |
162 | * the control and status registers. | |
163 | */ | |
91447636 | 164 | set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */ |
1c79356b A |
165 | |
166 | fninit(); | |
167 | status = fnstsw(); | |
168 | fnstcw(&control); | |
169 | ||
170 | if ((status & 0xff) == 0 && | |
55e303ae A |
171 | (control & 0x103f) == 0x3f) |
172 | { | |
55e303ae | 173 | /* Use FPU save/restore instructions if available */ |
0c530ab8 | 174 | if (cpuid_features() & CPUID_FEATURE_FXSR) { |
2d21ac55 | 175 | fp_kind = FP_FXSR; |
0c530ab8 | 176 | set_cr4(get_cr4() | CR4_FXS); |
0c530ab8 A |
177 | /* And allow SIMD instructions if present */ |
178 | if (cpuid_features() & CPUID_FEATURE_SSE) { | |
0c530ab8 A |
179 | set_cr4(get_cr4() | CR4_XMM); |
180 | } | |
0c530ab8 A |
181 | } else |
182 | panic("fpu is not FP_FXSR"); | |
55e303ae | 183 | |
1c79356b | 184 | /* |
0c530ab8 A |
185 | * initialze FPU to normal starting |
186 | * position so that we can take a snapshot | |
187 | * of that state and store it for future use | |
188 | * when we're asked for the FPU state of a | |
189 | * thread, and it hasn't initiated any yet | |
1c79356b | 190 | */ |
0c530ab8 A |
191 | fpinit(); |
192 | fxsave(&starting_fp_state.fx_save_state); | |
193 | ||
194 | /* | |
195 | * Trap wait instructions. Turn off FPU for now. | |
196 | */ | |
197 | set_cr0(get_cr0() | CR0_TS | CR0_MP); | |
1c79356b A |
198 | } |
199 | else | |
200 | { | |
201 | /* | |
202 | * NO FPU. | |
203 | */ | |
0c530ab8 | 204 | panic("fpu is not FP_FXSR"); |
1c79356b A |
205 | } |
206 | } | |
207 | ||
208 | /* | |
209 | * Initialize FP handling. | |
210 | */ | |
211 | void | |
212 | fpu_module_init(void) | |
213 | { | |
0c530ab8 A |
214 | struct x86_fpsave_state *new_ifps; |
215 | ||
216 | ifps_zone = zinit(sizeof(struct x86_fpsave_state), | |
b0d623f7 | 217 | thread_max * sizeof(struct x86_fpsave_state), |
0c530ab8 A |
218 | THREAD_CHUNK * sizeof(struct x86_fpsave_state), |
219 | "x86 fpsave state"); | |
220 | new_ifps = fp_state_alloc(); | |
221 | /* Determine MXCSR reserved bits */ | |
222 | configure_mxcsr_capability_mask(new_ifps); | |
223 | fp_state_free(new_ifps); | |
1c79356b A |
224 | } |
225 | ||
226 | /* | |
227 | * Free a FPU save area. | |
228 | * Called only when thread terminating - no locking necessary. | |
229 | */ | |
230 | void | |
2d21ac55 | 231 | fpu_free(struct x86_fpsave_state *fps) |
1c79356b | 232 | { |
0c530ab8 | 233 | fp_state_free(fps); |
1c79356b A |
234 | } |
235 | ||
55e303ae A |
236 | /* |
237 | * Set the floating-point state for a thread based | |
238 | * on the FXSave formatted data. This is basically | |
239 | * the same as fpu_set_state except it uses the | |
240 | * expanded data structure. | |
241 | * If the thread is not the current thread, it is | |
242 | * not running (held). Locking needed against | |
243 | * concurrent fpu_set_state or fpu_get_state. | |
244 | */ | |
245 | kern_return_t | |
246 | fpu_set_fxstate( | |
2d21ac55 | 247 | thread_t thr_act, |
0c530ab8 | 248 | thread_state_t tstate) |
55e303ae | 249 | { |
0c530ab8 A |
250 | struct x86_fpsave_state *ifps; |
251 | struct x86_fpsave_state *new_ifps; | |
252 | x86_float_state64_t *state; | |
253 | pcb_t pcb; | |
55e303ae | 254 | |
55e303ae | 255 | if (fp_kind == FP_NO) |
2d21ac55 | 256 | return KERN_FAILURE; |
0c530ab8 A |
257 | |
258 | state = (x86_float_state64_t *)tstate; | |
55e303ae | 259 | |
91447636 A |
260 | assert(thr_act != THREAD_NULL); |
261 | pcb = thr_act->machine.pcb; | |
55e303ae | 262 | |
0c530ab8 | 263 | if (state == NULL) { |
2d21ac55 A |
264 | /* |
265 | * new FPU state is 'invalid'. | |
266 | * Deallocate the fp state if it exists. | |
267 | */ | |
268 | simple_lock(&pcb->lock); | |
0c530ab8 A |
269 | |
270 | ifps = pcb->ifps; | |
271 | pcb->ifps = 0; | |
4452a7af | 272 | |
2d21ac55 | 273 | simple_unlock(&pcb->lock); |
0c530ab8 A |
274 | |
275 | if (ifps != 0) | |
2d21ac55 | 276 | fp_state_free(ifps); |
0c530ab8 | 277 | } else { |
2d21ac55 A |
278 | /* |
279 | * Valid state. Allocate the fp state if there is none. | |
280 | */ | |
281 | new_ifps = 0; | |
55e303ae | 282 | Retry: |
2d21ac55 | 283 | simple_lock(&pcb->lock); |
0c530ab8 A |
284 | |
285 | ifps = pcb->ifps; | |
2d21ac55 A |
286 | if (ifps == 0) { |
287 | if (new_ifps == 0) { | |
288 | simple_unlock(&pcb->lock); | |
289 | new_ifps = fp_state_alloc(); | |
290 | goto Retry; | |
55e303ae | 291 | } |
2d21ac55 A |
292 | ifps = new_ifps; |
293 | new_ifps = 0; | |
294 | pcb->ifps = ifps; | |
295 | } | |
296 | /* | |
297 | * now copy over the new data. | |
298 | */ | |
299 | bcopy((char *)&state->fpu_fcw, | |
0c530ab8 | 300 | (char *)&ifps->fx_save_state, sizeof(struct x86_fx_save)); |
4452a7af | 301 | |
0c530ab8 A |
302 | /* XXX The layout of the state set from user-space may need to be |
303 | * validated for consistency. | |
304 | */ | |
305 | ifps->fp_save_layout = thread_is_64bit(thr_act) ? FXSAVE64 : FXSAVE32; | |
2d21ac55 A |
306 | /* Mark the thread's floating point status as non-live. */ |
307 | /* Temporarily disabled: radar 4647827 | |
308 | * ifps->fp_valid = TRUE; | |
309 | */ | |
310 | ||
0c530ab8 A |
311 | /* |
312 | * Clear any reserved bits in the MXCSR to prevent a GPF | |
313 | * when issuing an FXRSTOR. | |
314 | */ | |
2d21ac55 | 315 | ifps->fx_save_state.fx_MXCSR &= mxcsr_capability_mask; |
6601e61a | 316 | |
2d21ac55 | 317 | simple_unlock(&pcb->lock); |
0c530ab8 | 318 | |
2d21ac55 A |
319 | if (new_ifps != 0) |
320 | fp_state_free(new_ifps); | |
0c530ab8 | 321 | } |
55e303ae A |
322 | return KERN_SUCCESS; |
323 | } | |
324 | ||
325 | /* | |
326 | * Get the floating-point state for a thread. | |
327 | * If the thread is not the current thread, it is | |
328 | * not running (held). Locking needed against | |
329 | * concurrent fpu_set_state or fpu_get_state. | |
330 | */ | |
331 | kern_return_t | |
332 | fpu_get_fxstate( | |
2d21ac55 | 333 | thread_t thr_act, |
0c530ab8 | 334 | thread_state_t tstate) |
55e303ae | 335 | { |
0c530ab8 A |
336 | struct x86_fpsave_state *ifps; |
337 | x86_float_state64_t *state; | |
338 | kern_return_t ret = KERN_FAILURE; | |
339 | pcb_t pcb; | |
55e303ae | 340 | |
0c530ab8 | 341 | if (fp_kind == FP_NO) |
2d21ac55 | 342 | return KERN_FAILURE; |
0c530ab8 A |
343 | |
344 | state = (x86_float_state64_t *)tstate; | |
55e303ae | 345 | |
91447636 A |
346 | assert(thr_act != THREAD_NULL); |
347 | pcb = thr_act->machine.pcb; | |
55e303ae A |
348 | |
349 | simple_lock(&pcb->lock); | |
0c530ab8 A |
350 | |
351 | ifps = pcb->ifps; | |
55e303ae | 352 | if (ifps == 0) { |
2d21ac55 | 353 | /* |
0c530ab8 A |
354 | * No valid floating-point state. |
355 | */ | |
2d21ac55 | 356 | bcopy((char *)&starting_fp_state.fx_save_state, |
0c530ab8 A |
357 | (char *)&state->fpu_fcw, sizeof(struct x86_fx_save)); |
358 | ||
359 | simple_unlock(&pcb->lock); | |
6601e61a | 360 | |
0c530ab8 A |
361 | return KERN_SUCCESS; |
362 | } | |
363 | /* | |
364 | * Make sure we`ve got the latest fp state info | |
365 | * If the live fpu state belongs to our target | |
366 | */ | |
2d21ac55 A |
367 | if (thr_act == current_thread()) { |
368 | boolean_t intr; | |
8f6c56a5 | 369 | |
0c530ab8 | 370 | intr = ml_set_interrupts_enabled(FALSE); |
89b3af67 | 371 | |
0c530ab8 A |
372 | clear_ts(); |
373 | fp_save(thr_act); | |
374 | clear_fpu(); | |
6601e61a | 375 | |
0c530ab8 | 376 | (void)ml_set_interrupts_enabled(intr); |
6601e61a | 377 | } |
0c530ab8 A |
378 | if (ifps->fp_valid) { |
379 | bcopy((char *)&ifps->fx_save_state, | |
380 | (char *)&state->fpu_fcw, sizeof(struct x86_fx_save)); | |
381 | ret = KERN_SUCCESS; | |
6601e61a | 382 | } |
0c530ab8 | 383 | simple_unlock(&pcb->lock); |
21362eb3 | 384 | |
0c530ab8 | 385 | return ret; |
6601e61a | 386 | } |
21362eb3 | 387 | |
0c530ab8 | 388 | |
2d21ac55 | 389 | |
6601e61a | 390 | /* |
0c530ab8 A |
391 | * the child thread is 'stopped' with the thread |
392 | * mutex held and is currently not known by anyone | |
393 | * so no way for fpu state to get manipulated by an | |
394 | * outside agency -> no need for pcb lock | |
6601e61a | 395 | */ |
0c530ab8 A |
396 | |
397 | void | |
398 | fpu_dup_fxstate( | |
399 | thread_t parent, | |
400 | thread_t child) | |
6601e61a | 401 | { |
0c530ab8 A |
402 | struct x86_fpsave_state *new_ifps = NULL; |
403 | boolean_t intr; | |
404 | pcb_t ppcb; | |
21362eb3 | 405 | |
0c530ab8 | 406 | ppcb = parent->machine.pcb; |
21362eb3 | 407 | |
0c530ab8 A |
408 | if (ppcb->ifps == NULL) |
409 | return; | |
4452a7af | 410 | |
0c530ab8 A |
411 | if (child->machine.pcb->ifps) |
412 | panic("fpu_dup_fxstate: child's ifps non-null"); | |
4452a7af | 413 | |
0c530ab8 | 414 | new_ifps = fp_state_alloc(); |
5d5c5d0d | 415 | |
0c530ab8 | 416 | simple_lock(&ppcb->lock); |
6601e61a | 417 | |
0c530ab8 A |
418 | if (ppcb->ifps != NULL) { |
419 | /* | |
420 | * Make sure we`ve got the latest fp state info | |
421 | */ | |
422 | intr = ml_set_interrupts_enabled(FALSE); | |
6601e61a | 423 | |
0c530ab8 A |
424 | clear_ts(); |
425 | fp_save(parent); | |
426 | clear_fpu(); | |
6601e61a | 427 | |
0c530ab8 | 428 | (void)ml_set_interrupts_enabled(intr); |
6601e61a | 429 | |
0c530ab8 A |
430 | if (ppcb->ifps->fp_valid) { |
431 | child->machine.pcb->ifps = new_ifps; | |
432 | ||
433 | bcopy((char *)&(ppcb->ifps->fx_save_state), | |
434 | (char *)&(child->machine.pcb->ifps->fx_save_state), sizeof(struct x86_fx_save)); | |
435 | ||
436 | new_ifps->fp_save_layout = ppcb->ifps->fp_save_layout; | |
2d21ac55 A |
437 | /* Mark the new fp saved state as non-live. */ |
438 | /* Temporarily disabled: radar 4647827 | |
439 | * new_ifps->fp_valid = TRUE; | |
440 | */ | |
0c530ab8 A |
441 | /* |
442 | * Clear any reserved bits in the MXCSR to prevent a GPF | |
443 | * when issuing an FXRSTOR. | |
444 | */ | |
445 | new_ifps->fx_save_state.fx_MXCSR &= mxcsr_capability_mask; | |
446 | new_ifps = NULL; | |
447 | } | |
6601e61a | 448 | } |
0c530ab8 | 449 | simple_unlock(&ppcb->lock); |
89b3af67 | 450 | |
0c530ab8 A |
451 | if (new_ifps != NULL) |
452 | fp_state_free(new_ifps); | |
6601e61a | 453 | } |
4452a7af | 454 | |
0c530ab8 | 455 | |
1c79356b A |
456 | /* |
457 | * Initialize FPU. | |
458 | * | |
1c79356b A |
459 | */ |
460 | void | |
461 | fpinit(void) | |
462 | { | |
463 | unsigned short control; | |
464 | ||
1c79356b A |
465 | clear_ts(); |
466 | fninit(); | |
467 | fnstcw(&control); | |
468 | control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */ | |
0c530ab8 | 469 | control |= (FPC_PC_64 | /* Set precision */ |
1c79356b A |
470 | FPC_RC_RN | /* round-to-nearest */ |
471 | FPC_ZE | /* Suppress zero-divide */ | |
472 | FPC_OE | /* and overflow */ | |
473 | FPC_UE | /* underflow */ | |
474 | FPC_IE | /* Allow NaNQs and +-INF */ | |
475 | FPC_DE | /* Allow denorms as operands */ | |
476 | FPC_PE); /* No trap for precision loss */ | |
477 | fldcw(control); | |
0c530ab8 A |
478 | |
479 | /* Initialize SSE/SSE2 */ | |
2d21ac55 | 480 | __builtin_ia32_ldmxcsr(0x1f80); |
b0d623f7 | 481 | } |
1c79356b A |
482 | |
483 | /* | |
484 | * Coprocessor not present. | |
485 | */ | |
486 | ||
487 | void | |
488 | fpnoextflt(void) | |
489 | { | |
0c530ab8 | 490 | boolean_t intr; |
2d21ac55 A |
491 | thread_t thr_act; |
492 | pcb_t pcb; | |
493 | struct x86_fpsave_state *ifps = 0; | |
494 | ||
495 | thr_act = current_thread(); | |
496 | pcb = thr_act->machine.pcb; | |
497 | ||
498 | if (pcb->ifps == 0 && !get_interrupt_level()) | |
499 | ifps = fp_state_alloc(); | |
4452a7af | 500 | |
0c530ab8 A |
501 | intr = ml_set_interrupts_enabled(FALSE); |
502 | ||
503 | clear_ts(); /* Enable FPU use */ | |
504 | ||
505 | if (get_interrupt_level()) { | |
506 | /* | |
507 | * Save current coprocessor context if valid | |
508 | * Initialize coprocessor live context | |
509 | */ | |
2d21ac55 | 510 | fp_save(thr_act); |
0c530ab8 A |
511 | fpinit(); |
512 | } else { | |
2d21ac55 A |
513 | if (pcb->ifps == 0) { |
514 | pcb->ifps = ifps; | |
515 | ifps = 0; | |
516 | } | |
0c530ab8 A |
517 | /* |
518 | * Load this thread`s state into coprocessor live context. | |
519 | */ | |
2d21ac55 | 520 | fp_load(thr_act); |
0c530ab8 | 521 | } |
0c530ab8 | 522 | (void)ml_set_interrupts_enabled(intr); |
2d21ac55 A |
523 | |
524 | if (ifps) | |
525 | fp_state_free(ifps); | |
1c79356b A |
526 | } |
527 | ||
528 | /* | |
529 | * FPU overran end of segment. | |
530 | * Re-initialize FPU. Floating point state is not valid. | |
531 | */ | |
532 | ||
533 | void | |
534 | fpextovrflt(void) | |
535 | { | |
0c530ab8 A |
536 | thread_t thr_act = current_thread(); |
537 | pcb_t pcb; | |
538 | struct x86_fpsave_state *ifps; | |
539 | boolean_t intr; | |
540 | ||
541 | intr = ml_set_interrupts_enabled(FALSE); | |
542 | ||
543 | if (get_interrupt_level()) | |
2d21ac55 | 544 | panic("FPU segment overrun exception at interrupt context\n"); |
0c530ab8 A |
545 | if (current_task() == kernel_task) |
546 | panic("FPU segment overrun exception in kernel thread context\n"); | |
1c79356b | 547 | |
1c79356b A |
548 | /* |
549 | * This is a non-recoverable error. | |
550 | * Invalidate the thread`s FPU state. | |
551 | */ | |
91447636 | 552 | pcb = thr_act->machine.pcb; |
1c79356b | 553 | simple_lock(&pcb->lock); |
0c530ab8 A |
554 | ifps = pcb->ifps; |
555 | pcb->ifps = 0; | |
1c79356b A |
556 | simple_unlock(&pcb->lock); |
557 | ||
558 | /* | |
559 | * Re-initialize the FPU. | |
560 | */ | |
561 | clear_ts(); | |
562 | fninit(); | |
563 | ||
564 | /* | |
565 | * And disable access. | |
566 | */ | |
567 | clear_fpu(); | |
568 | ||
0c530ab8 A |
569 | (void)ml_set_interrupts_enabled(intr); |
570 | ||
1c79356b | 571 | if (ifps) |
91447636 | 572 | zfree(ifps_zone, ifps); |
1c79356b A |
573 | |
574 | /* | |
575 | * Raise exception. | |
576 | */ | |
577 | i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0); | |
578 | /*NOTREACHED*/ | |
579 | } | |
580 | ||
581 | /* | |
582 | * FPU error. Called by AST. | |
583 | */ | |
584 | ||
585 | void | |
586 | fpexterrflt(void) | |
587 | { | |
0c530ab8 A |
588 | thread_t thr_act = current_thread(); |
589 | struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps; | |
590 | boolean_t intr; | |
591 | ||
592 | intr = ml_set_interrupts_enabled(FALSE); | |
593 | ||
594 | if (get_interrupt_level()) | |
595 | panic("FPU error exception at interrupt context\n"); | |
596 | if (current_task() == kernel_task) | |
597 | panic("FPU error exception in kernel thread context\n"); | |
1c79356b | 598 | |
1c79356b A |
599 | /* |
600 | * Save the FPU state and turn off the FPU. | |
601 | */ | |
602 | fp_save(thr_act); | |
1c79356b | 603 | |
0c530ab8 A |
604 | (void)ml_set_interrupts_enabled(intr); |
605 | ||
1c79356b A |
606 | /* |
607 | * Raise FPU exception. | |
0c530ab8 | 608 | * Locking not needed on pcb->ifps, |
1c79356b A |
609 | * since thread is running. |
610 | */ | |
611 | i386_exception(EXC_ARITHMETIC, | |
612 | EXC_I386_EXTERR, | |
0c530ab8 A |
613 | ifps->fx_save_state.fx_status); |
614 | ||
1c79356b A |
615 | /*NOTREACHED*/ |
616 | } | |
617 | ||
618 | /* | |
619 | * Save FPU state. | |
620 | * | |
621 | * Locking not needed: | |
622 | * . if called from fpu_get_state, pcb already locked. | |
623 | * . if called from fpnoextflt or fp_intr, we are single-cpu | |
624 | * . otherwise, thread is running. | |
0c530ab8 | 625 | * N.B.: Must be called with interrupts disabled |
1c79356b | 626 | */ |
0c530ab8 | 627 | |
1c79356b A |
628 | void |
629 | fp_save( | |
91447636 | 630 | thread_t thr_act) |
1c79356b | 631 | { |
0c530ab8 A |
632 | pcb_t pcb = thr_act->machine.pcb; |
633 | struct x86_fpsave_state *ifps = pcb->ifps; | |
634 | ||
1c79356b | 635 | if (ifps != 0 && !ifps->fp_valid) { |
0c530ab8 A |
636 | assert((get_cr0() & CR0_TS) == 0); |
637 | /* registers are in FPU */ | |
638 | ifps->fp_valid = TRUE; | |
639 | ||
b0d623f7 | 640 | #if defined(__i386__) |
0c530ab8 A |
641 | if (!thread_is_64bit(thr_act)) { |
642 | /* save the compatibility/legacy mode XMM+x87 state */ | |
643 | fxsave(&ifps->fx_save_state); | |
644 | ifps->fp_save_layout = FXSAVE32; | |
645 | } | |
646 | else { | |
647 | fxsave64(&ifps->fx_save_state); | |
648 | ifps->fp_save_layout = FXSAVE64; | |
649 | } | |
b0d623f7 A |
650 | #elif defined(__x86_64__) |
651 | fxsave(&ifps->fx_save_state); | |
652 | ifps->fp_save_layout = thread_is_64bit(thr_act) ? FXSAVE64 : FXSAVE32; | |
653 | #endif | |
1c79356b A |
654 | } |
655 | } | |
656 | ||
657 | /* | |
658 | * Restore FPU state from PCB. | |
659 | * | |
660 | * Locking not needed; always called on the current thread. | |
661 | */ | |
662 | ||
663 | void | |
664 | fp_load( | |
91447636 | 665 | thread_t thr_act) |
1c79356b | 666 | { |
0c530ab8 A |
667 | pcb_t pcb = thr_act->machine.pcb; |
668 | struct x86_fpsave_state *ifps; | |
669 | ||
670 | ifps = pcb->ifps; | |
671 | if (ifps == 0 || ifps->fp_valid == FALSE) { | |
672 | if (ifps == 0) { | |
673 | /* FIXME: This allocation mechanism should be revised | |
674 | * for scenarios where interrupts are disabled. | |
675 | */ | |
676 | ifps = fp_state_alloc(); | |
677 | pcb->ifps = ifps; | |
678 | } | |
679 | fpinit(); | |
1c79356b | 680 | } else { |
0c530ab8 | 681 | assert(ifps->fp_save_layout == FXSAVE32 || ifps->fp_save_layout == FXSAVE64); |
b0d623f7 | 682 | #if defined(__i386__) |
0c530ab8 A |
683 | if (ifps->fp_save_layout == FXSAVE32) { |
684 | /* Restore the compatibility/legacy mode XMM+x87 state */ | |
685 | fxrstor(&ifps->fx_save_state); | |
686 | } | |
687 | else if (ifps->fp_save_layout == FXSAVE64) { | |
688 | fxrstor64(&ifps->fx_save_state); | |
689 | } | |
b0d623f7 A |
690 | #elif defined(__x86_64__) |
691 | fxrstor(&ifps->fx_save_state); | |
692 | #endif | |
1c79356b A |
693 | } |
694 | ifps->fp_valid = FALSE; /* in FPU */ | |
695 | } | |
696 | ||
55e303ae | 697 | |
1c79356b A |
698 | |
699 | /* | |
91447636 | 700 | * fpflush(thread_t) |
1c79356b A |
701 | * Flush the current act's state, if needed |
702 | * (used by thread_terminate_self to ensure fp faults | |
703 | * aren't satisfied by overly general trap code in the | |
704 | * context of the reaper thread) | |
705 | */ | |
706 | void | |
91447636 | 707 | fpflush(__unused thread_t thr_act) |
1c79356b | 708 | { |
1c79356b | 709 | /* not needed on MP x86s; fp not lazily evaluated */ |
1c79356b A |
710 | } |
711 | ||
1c79356b | 712 | /* |
0c530ab8 A |
713 | * SSE arithmetic exception handling code. |
714 | * Basically the same as the x87 exception handler with a different subtype | |
1c79356b A |
715 | */ |
716 | ||
717 | void | |
0c530ab8 | 718 | fpSSEexterrflt(void) |
1c79356b | 719 | { |
0c530ab8 A |
720 | thread_t thr_act = current_thread(); |
721 | struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps; | |
722 | boolean_t intr; | |
4452a7af | 723 | |
0c530ab8 A |
724 | intr = ml_set_interrupts_enabled(FALSE); |
725 | ||
726 | if (get_interrupt_level()) | |
727 | panic("SSE exception at interrupt context\n"); | |
728 | if (current_task() == kernel_task) | |
729 | panic("SSE exception in kernel thread context\n"); | |
1c79356b A |
730 | |
731 | /* | |
0c530ab8 | 732 | * Save the FPU state and turn off the FPU. |
1c79356b | 733 | */ |
1c79356b | 734 | fp_save(thr_act); |
1c79356b | 735 | |
0c530ab8 | 736 | (void)ml_set_interrupts_enabled(intr); |
1c79356b | 737 | /* |
0c530ab8 A |
738 | * Raise FPU exception. |
739 | * Locking not needed on pcb->ifps, | |
740 | * since thread is running. | |
1c79356b | 741 | */ |
0c530ab8 A |
742 | assert(ifps->fp_save_layout == FXSAVE32 || ifps->fp_save_layout == FXSAVE64); |
743 | i386_exception(EXC_ARITHMETIC, | |
744 | EXC_I386_SSEEXTERR, | |
b0d623f7 | 745 | ifps->fx_save_state.fx_MXCSR); |
0c530ab8 A |
746 | /*NOTREACHED*/ |
747 | } | |
748 | ||
749 | ||
750 | void | |
751 | fp_setvalid(boolean_t value) { | |
752 | thread_t thr_act = current_thread(); | |
753 | struct x86_fpsave_state *ifps = thr_act->machine.pcb->ifps; | |
754 | ||
755 | if (ifps) { | |
756 | ifps->fp_valid = value; | |
757 | ||
758 | if (value == TRUE) | |
759 | clear_fpu(); | |
760 | } | |
1c79356b | 761 | } |