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