]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
ff6e181a 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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
1c79356b | 12 | * |
ff6e181a A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
ff6e181a A |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
1c79356b A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | /* | |
24 | * @OSF_COPYRIGHT@ | |
25 | */ | |
26 | /* | |
27 | * Mach Operating System | |
28 | * Copyright (c) 1992-1990 Carnegie Mellon University | |
29 | * All Rights Reserved. | |
30 | * | |
31 | * Permission to use, copy, modify and distribute this software and its | |
32 | * documentation is hereby granted, provided that both the copyright | |
33 | * notice and this permission notice appear in all copies of the | |
34 | * software, derivative works or modified versions, and any portions | |
35 | * thereof, and that both notices appear in supporting documentation. | |
36 | * | |
37 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
38 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
39 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
40 | * | |
41 | * Carnegie Mellon requests users of this software to return to | |
42 | * | |
43 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
44 | * School of Computer Science | |
45 | * Carnegie Mellon University | |
46 | * Pittsburgh PA 15213-3890 | |
47 | * | |
48 | * any improvements or extensions that they make and grant Carnegie Mellon | |
49 | * the rights to redistribute these changes. | |
50 | */ | |
51 | /* | |
52 | */ | |
53 | ||
1c79356b A |
54 | #include <platforms.h> |
55 | ||
56 | #include <mach/exception_types.h> | |
57 | #include <mach/i386/thread_status.h> | |
58 | #include <mach/i386/fp_reg.h> | |
59 | ||
60 | #include <kern/mach_param.h> | |
91447636 | 61 | #include <kern/processor.h> |
1c79356b A |
62 | #include <kern/thread.h> |
63 | #include <kern/zalloc.h> | |
64 | #include <kern/misc_protos.h> | |
65 | #include <kern/spl.h> | |
66 | #include <kern/assert.h> | |
67 | ||
68 | #include <i386/thread.h> | |
69 | #include <i386/fpu.h> | |
70 | #include <i386/trap.h> | |
71 | #include <i386/pio.h> | |
55e303ae | 72 | #include <i386/cpuid.h> |
1c79356b A |
73 | #include <i386/misc_protos.h> |
74 | ||
75 | #if 0 | |
76 | #include <i386/ipl.h> | |
77 | extern int curr_ipl; | |
78 | #define ASSERT_IPL(L) \ | |
79 | { \ | |
80 | if (curr_ipl != L) { \ | |
81 | printf("IPL is %d, expected %d\n", curr_ipl, L); \ | |
82 | panic("fpu: wrong ipl"); \ | |
83 | } \ | |
84 | } | |
85 | #else | |
86 | #define ASSERT_IPL(L) | |
87 | #endif | |
88 | ||
89 | int fp_kind = FP_387; /* 80387 present */ | |
90 | zone_t ifps_zone; /* zone for FPU save area */ | |
91 | ||
1c79356b A |
92 | #define clear_fpu() \ |
93 | { \ | |
94 | set_ts(); \ | |
1c79356b A |
95 | } |
96 | ||
55e303ae | 97 | #define ALIGNED(addr,size) (((unsigned)(addr)&((size)-1))==0) |
1c79356b A |
98 | |
99 | /* Forward */ | |
100 | ||
101 | extern void fpinit(void); | |
102 | extern void fp_save( | |
91447636 | 103 | thread_t thr_act); |
1c79356b | 104 | extern void fp_load( |
91447636 | 105 | thread_t thr_act); |
1c79356b A |
106 | |
107 | /* | |
108 | * Look for FPU and initialize it. | |
109 | * Called on each CPU. | |
110 | */ | |
111 | void | |
112 | init_fpu(void) | |
113 | { | |
114 | unsigned short status, control; | |
115 | ||
116 | /* | |
117 | * Check for FPU by initializing it, | |
118 | * then trying to read the correct bit patterns from | |
119 | * the control and status registers. | |
120 | */ | |
91447636 | 121 | set_cr0((get_cr0() & ~(CR0_EM|CR0_TS)) | CR0_NE); /* allow use of FPU */ |
1c79356b A |
122 | |
123 | fninit(); | |
124 | status = fnstsw(); | |
125 | fnstcw(&control); | |
126 | ||
127 | if ((status & 0xff) == 0 && | |
55e303ae A |
128 | (control & 0x103f) == 0x3f) |
129 | { | |
130 | fp_kind = FP_387; /* assume we have a 387 compatible instruction set */ | |
131 | /* Use FPU save/restore instructions if available */ | |
132 | if (cpuid_features() & CPUID_FEATURE_FXSR) { | |
133 | fp_kind = FP_FXSR; | |
134 | set_cr4(get_cr4() | CR4_FXS); | |
135 | printf("Enabling XMM register save/restore"); | |
136 | /* And allow SIMD instructions if present */ | |
137 | if (cpuid_features() & CPUID_FEATURE_SSE) { | |
138 | printf(" and SSE/SSE2"); | |
139 | set_cr4(get_cr4() | CR4_XMM); | |
140 | } | |
141 | printf(" opcodes\n"); | |
1c79356b | 142 | } |
55e303ae | 143 | |
1c79356b A |
144 | /* |
145 | * Trap wait instructions. Turn off FPU for now. | |
146 | */ | |
147 | set_cr0(get_cr0() | CR0_TS | CR0_MP); | |
148 | } | |
149 | else | |
150 | { | |
151 | /* | |
152 | * NO FPU. | |
153 | */ | |
154 | fp_kind = FP_NO; | |
155 | set_cr0(get_cr0() | CR0_EM); | |
156 | } | |
157 | } | |
158 | ||
159 | /* | |
160 | * Initialize FP handling. | |
161 | */ | |
162 | void | |
163 | fpu_module_init(void) | |
164 | { | |
165 | ifps_zone = zinit(sizeof(struct i386_fpsave_state), | |
166 | THREAD_MAX * sizeof(struct i386_fpsave_state), | |
167 | THREAD_CHUNK * sizeof(struct i386_fpsave_state), | |
168 | "i386 fpsave state"); | |
169 | } | |
170 | ||
171 | /* | |
172 | * Free a FPU save area. | |
173 | * Called only when thread terminating - no locking necessary. | |
174 | */ | |
175 | void | |
91447636 | 176 | fpu_free(fps) |
1c79356b A |
177 | struct i386_fpsave_state *fps; |
178 | { | |
179 | ASSERT_IPL(SPL0); | |
91447636 | 180 | zfree(ifps_zone, fps); |
1c79356b A |
181 | } |
182 | ||
55e303ae A |
183 | /* |
184 | * Set the floating-point state for a thread based | |
185 | * on the FXSave formatted data. This is basically | |
186 | * the same as fpu_set_state except it uses the | |
187 | * expanded data structure. | |
188 | * If the thread is not the current thread, it is | |
189 | * not running (held). Locking needed against | |
190 | * concurrent fpu_set_state or fpu_get_state. | |
191 | */ | |
192 | kern_return_t | |
193 | fpu_set_fxstate( | |
91447636 | 194 | thread_t thr_act, |
55e303ae A |
195 | struct i386_float_state *state) |
196 | { | |
197 | register pcb_t pcb; | |
198 | register struct i386_fpsave_state *ifps; | |
199 | register struct i386_fpsave_state *new_ifps; | |
200 | ||
201 | ASSERT_IPL(SPL0); | |
202 | if (fp_kind == FP_NO) | |
203 | return KERN_FAILURE; | |
204 | ||
205 | if (state->fpkind != FP_FXSR) { | |
206 | /* strange if this happens, but in case someone builds one of these manually... */ | |
207 | return fpu_set_state(thr_act, state); | |
208 | } | |
209 | ||
91447636 A |
210 | assert(thr_act != THREAD_NULL); |
211 | pcb = thr_act->machine.pcb; | |
55e303ae | 212 | |
55e303ae A |
213 | if (state->initialized == 0) { |
214 | /* | |
215 | * new FPU state is 'invalid'. | |
216 | * Deallocate the fp state if it exists. | |
217 | */ | |
218 | simple_lock(&pcb->lock); | |
219 | ifps = pcb->ims.ifps; | |
220 | pcb->ims.ifps = 0; | |
221 | simple_unlock(&pcb->lock); | |
222 | ||
223 | if (ifps != 0) { | |
91447636 | 224 | zfree(ifps_zone, ifps); |
55e303ae A |
225 | } |
226 | } | |
227 | else { | |
228 | /* | |
229 | * Valid state. Allocate the fp state if there is none. | |
230 | */ | |
231 | ||
232 | new_ifps = 0; | |
233 | Retry: | |
234 | simple_lock(&pcb->lock); | |
235 | ifps = pcb->ims.ifps; | |
236 | if (ifps == 0) { | |
237 | if (new_ifps == 0) { | |
238 | simple_unlock(&pcb->lock); | |
239 | new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone); | |
240 | assert(ALIGNED(new_ifps,16)); | |
241 | goto Retry; | |
242 | } | |
243 | ifps = new_ifps; | |
244 | new_ifps = 0; | |
245 | bzero((char *)ifps, sizeof *ifps); | |
246 | pcb->ims.ifps = ifps; | |
247 | } | |
248 | ||
249 | /* | |
250 | * now copy over the new data. | |
251 | */ | |
252 | bcopy((char *)&state->hw_state[0], (char *)&ifps->fx_save_state, sizeof(struct i386_fx_save)); | |
253 | ifps->fp_save_flavor = FP_FXSR; | |
254 | simple_unlock(&pcb->lock); | |
255 | if (new_ifps != 0) | |
91447636 | 256 | zfree(ifps_zone, ifps); |
55e303ae A |
257 | } |
258 | ||
259 | return KERN_SUCCESS; | |
260 | } | |
261 | ||
262 | /* | |
263 | * Get the floating-point state for a thread. | |
264 | * If the thread is not the current thread, it is | |
265 | * not running (held). Locking needed against | |
266 | * concurrent fpu_set_state or fpu_get_state. | |
267 | */ | |
268 | kern_return_t | |
269 | fpu_get_fxstate( | |
91447636 | 270 | thread_t thr_act, |
55e303ae A |
271 | register struct i386_float_state *state) |
272 | { | |
273 | register pcb_t pcb; | |
274 | register struct i386_fpsave_state *ifps; | |
275 | ||
276 | ASSERT_IPL(SPL0); | |
91447636 | 277 | if (fp_kind == FP_NO) { |
55e303ae | 278 | return KERN_FAILURE; |
91447636 A |
279 | } else if (fp_kind == FP_387) { |
280 | return fpu_get_state(thr_act, state); | |
281 | } | |
55e303ae | 282 | |
91447636 A |
283 | assert(thr_act != THREAD_NULL); |
284 | pcb = thr_act->machine.pcb; | |
55e303ae A |
285 | |
286 | simple_lock(&pcb->lock); | |
287 | ifps = pcb->ims.ifps; | |
288 | if (ifps == 0) { | |
289 | /* | |
290 | * No valid floating-point state. | |
291 | */ | |
292 | simple_unlock(&pcb->lock); | |
293 | bzero((char *)state, sizeof(struct i386_float_state)); | |
294 | return KERN_SUCCESS; | |
295 | } | |
296 | ||
297 | /* Make sure we`ve got the latest fp state info */ | |
298 | /* If the live fpu state belongs to our target */ | |
91447636 | 299 | if (thr_act == current_thread()) |
55e303ae A |
300 | { |
301 | clear_ts(); | |
302 | fp_save(thr_act); | |
303 | clear_fpu(); | |
304 | } | |
305 | ||
306 | state->fpkind = fp_kind; | |
307 | state->exc_status = 0; | |
308 | state->initialized = ifps->fp_valid; | |
309 | bcopy( (char *)&ifps->fx_save_state, (char *)&state->hw_state[0], sizeof(struct i386_fx_save)); | |
310 | ||
311 | simple_unlock(&pcb->lock); | |
312 | ||
313 | return KERN_SUCCESS; | |
314 | } | |
315 | ||
1c79356b A |
316 | /* |
317 | * Set the floating-point state for a thread. | |
318 | * If the thread is not the current thread, it is | |
319 | * not running (held). Locking needed against | |
320 | * concurrent fpu_set_state or fpu_get_state. | |
321 | */ | |
322 | kern_return_t | |
323 | fpu_set_state( | |
91447636 | 324 | thread_t thr_act, |
1c79356b A |
325 | struct i386_float_state *state) |
326 | { | |
327 | register pcb_t pcb; | |
328 | register struct i386_fpsave_state *ifps; | |
329 | register struct i386_fpsave_state *new_ifps; | |
330 | ||
331 | ASSERT_IPL(SPL0); | |
332 | if (fp_kind == FP_NO) | |
333 | return KERN_FAILURE; | |
334 | ||
91447636 A |
335 | assert(thr_act != THREAD_NULL); |
336 | pcb = thr_act->machine.pcb; | |
1c79356b A |
337 | |
338 | if (state->initialized == 0) { | |
339 | /* | |
340 | * new FPU state is 'invalid'. | |
341 | * Deallocate the fp state if it exists. | |
342 | */ | |
343 | simple_lock(&pcb->lock); | |
344 | ifps = pcb->ims.ifps; | |
345 | pcb->ims.ifps = 0; | |
346 | simple_unlock(&pcb->lock); | |
347 | ||
348 | if (ifps != 0) { | |
91447636 | 349 | zfree(ifps_zone, ifps); |
1c79356b A |
350 | } |
351 | } | |
352 | else { | |
353 | /* | |
354 | * Valid state. Allocate the fp state if there is none. | |
355 | */ | |
356 | register struct i386_fp_save *user_fp_state; | |
357 | register struct i386_fp_regs *user_fp_regs; | |
358 | ||
359 | user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; | |
360 | user_fp_regs = (struct i386_fp_regs *) | |
361 | &state->hw_state[sizeof(struct i386_fp_save)]; | |
362 | ||
363 | new_ifps = 0; | |
364 | Retry: | |
365 | simple_lock(&pcb->lock); | |
366 | ifps = pcb->ims.ifps; | |
367 | if (ifps == 0) { | |
368 | if (new_ifps == 0) { | |
369 | simple_unlock(&pcb->lock); | |
370 | new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone); | |
55e303ae | 371 | assert(ALIGNED(new_ifps,16)); |
1c79356b A |
372 | goto Retry; |
373 | } | |
374 | ifps = new_ifps; | |
375 | new_ifps = 0; | |
55e303ae | 376 | bzero((char *)ifps, sizeof *ifps); // zero ALL fields first |
1c79356b A |
377 | pcb->ims.ifps = ifps; |
378 | } | |
379 | ||
380 | /* | |
381 | * Ensure that reserved parts of the environment are 0. | |
382 | */ | |
383 | bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save)); | |
384 | ||
385 | ifps->fp_save_state.fp_control = user_fp_state->fp_control; | |
386 | ifps->fp_save_state.fp_status = user_fp_state->fp_status; | |
387 | ifps->fp_save_state.fp_tag = user_fp_state->fp_tag; | |
388 | ifps->fp_save_state.fp_eip = user_fp_state->fp_eip; | |
389 | ifps->fp_save_state.fp_cs = user_fp_state->fp_cs; | |
390 | ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode; | |
391 | ifps->fp_save_state.fp_dp = user_fp_state->fp_dp; | |
392 | ifps->fp_save_state.fp_ds = user_fp_state->fp_ds; | |
393 | ifps->fp_regs = *user_fp_regs; | |
55e303ae | 394 | ifps->fp_save_flavor = FP_387; |
1c79356b A |
395 | simple_unlock(&pcb->lock); |
396 | if (new_ifps != 0) | |
91447636 | 397 | zfree(ifps_zone, ifps); |
1c79356b A |
398 | } |
399 | ||
400 | return KERN_SUCCESS; | |
401 | } | |
402 | ||
403 | /* | |
404 | * Get the floating-point state for a thread. | |
405 | * If the thread is not the current thread, it is | |
406 | * not running (held). Locking needed against | |
407 | * concurrent fpu_set_state or fpu_get_state. | |
408 | */ | |
409 | kern_return_t | |
410 | fpu_get_state( | |
91447636 | 411 | thread_t thr_act, |
1c79356b A |
412 | register struct i386_float_state *state) |
413 | { | |
414 | register pcb_t pcb; | |
415 | register struct i386_fpsave_state *ifps; | |
416 | ||
417 | ASSERT_IPL(SPL0); | |
418 | if (fp_kind == FP_NO) | |
419 | return KERN_FAILURE; | |
420 | ||
91447636 A |
421 | assert(thr_act != THREAD_NULL); |
422 | pcb = thr_act->machine.pcb; | |
1c79356b A |
423 | |
424 | simple_lock(&pcb->lock); | |
425 | ifps = pcb->ims.ifps; | |
426 | if (ifps == 0) { | |
427 | /* | |
428 | * No valid floating-point state. | |
429 | */ | |
430 | simple_unlock(&pcb->lock); | |
431 | bzero((char *)state, sizeof(struct i386_float_state)); | |
432 | return KERN_SUCCESS; | |
433 | } | |
434 | ||
435 | /* Make sure we`ve got the latest fp state info */ | |
436 | /* If the live fpu state belongs to our target */ | |
91447636 | 437 | if (thr_act == current_thread()) |
1c79356b A |
438 | { |
439 | clear_ts(); | |
440 | fp_save(thr_act); | |
441 | clear_fpu(); | |
442 | } | |
443 | ||
444 | state->fpkind = fp_kind; | |
445 | state->exc_status = 0; | |
446 | ||
447 | { | |
448 | register struct i386_fp_save *user_fp_state; | |
449 | register struct i386_fp_regs *user_fp_regs; | |
450 | ||
451 | state->initialized = ifps->fp_valid; | |
452 | ||
453 | user_fp_state = (struct i386_fp_save *) &state->hw_state[0]; | |
454 | user_fp_regs = (struct i386_fp_regs *) | |
455 | &state->hw_state[sizeof(struct i386_fp_save)]; | |
456 | ||
457 | /* | |
458 | * Ensure that reserved parts of the environment are 0. | |
459 | */ | |
460 | bzero((char *)user_fp_state, sizeof(struct i386_fp_save)); | |
461 | ||
462 | user_fp_state->fp_control = ifps->fp_save_state.fp_control; | |
463 | user_fp_state->fp_status = ifps->fp_save_state.fp_status; | |
464 | user_fp_state->fp_tag = ifps->fp_save_state.fp_tag; | |
465 | user_fp_state->fp_eip = ifps->fp_save_state.fp_eip; | |
466 | user_fp_state->fp_cs = ifps->fp_save_state.fp_cs; | |
467 | user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode; | |
468 | user_fp_state->fp_dp = ifps->fp_save_state.fp_dp; | |
469 | user_fp_state->fp_ds = ifps->fp_save_state.fp_ds; | |
470 | *user_fp_regs = ifps->fp_regs; | |
471 | } | |
472 | simple_unlock(&pcb->lock); | |
473 | ||
474 | return KERN_SUCCESS; | |
475 | } | |
476 | ||
477 | /* | |
478 | * Initialize FPU. | |
479 | * | |
480 | * Raise exceptions for: | |
481 | * invalid operation | |
482 | * divide by zero | |
483 | * overflow | |
484 | * | |
485 | * Use 53-bit precision. | |
486 | */ | |
487 | void | |
488 | fpinit(void) | |
489 | { | |
490 | unsigned short control; | |
491 | ||
492 | ASSERT_IPL(SPL0); | |
493 | clear_ts(); | |
494 | fninit(); | |
495 | fnstcw(&control); | |
496 | control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */ | |
497 | control |= (FPC_PC_53 | /* Set precision */ | |
498 | FPC_RC_RN | /* round-to-nearest */ | |
499 | FPC_ZE | /* Suppress zero-divide */ | |
500 | FPC_OE | /* and overflow */ | |
501 | FPC_UE | /* underflow */ | |
502 | FPC_IE | /* Allow NaNQs and +-INF */ | |
503 | FPC_DE | /* Allow denorms as operands */ | |
504 | FPC_PE); /* No trap for precision loss */ | |
505 | fldcw(control); | |
506 | } | |
507 | ||
508 | /* | |
509 | * Coprocessor not present. | |
510 | */ | |
511 | ||
512 | void | |
513 | fpnoextflt(void) | |
514 | { | |
515 | /* | |
516 | * Enable FPU use. | |
517 | */ | |
518 | ASSERT_IPL(SPL0); | |
519 | clear_ts(); | |
1c79356b A |
520 | |
521 | /* | |
522 | * Load this thread`s state into the FPU. | |
523 | */ | |
91447636 | 524 | fp_load(current_thread()); |
1c79356b A |
525 | } |
526 | ||
527 | /* | |
528 | * FPU overran end of segment. | |
529 | * Re-initialize FPU. Floating point state is not valid. | |
530 | */ | |
531 | ||
532 | void | |
533 | fpextovrflt(void) | |
534 | { | |
91447636 | 535 | register thread_t thr_act = current_thread(); |
1c79356b A |
536 | register pcb_t pcb; |
537 | register struct i386_fpsave_state *ifps; | |
538 | ||
1c79356b A |
539 | /* |
540 | * This is a non-recoverable error. | |
541 | * Invalidate the thread`s FPU state. | |
542 | */ | |
91447636 | 543 | pcb = thr_act->machine.pcb; |
1c79356b A |
544 | simple_lock(&pcb->lock); |
545 | ifps = pcb->ims.ifps; | |
546 | pcb->ims.ifps = 0; | |
547 | simple_unlock(&pcb->lock); | |
548 | ||
549 | /* | |
550 | * Re-initialize the FPU. | |
551 | */ | |
552 | clear_ts(); | |
553 | fninit(); | |
554 | ||
555 | /* | |
556 | * And disable access. | |
557 | */ | |
558 | clear_fpu(); | |
559 | ||
560 | if (ifps) | |
91447636 | 561 | zfree(ifps_zone, ifps); |
1c79356b A |
562 | |
563 | /* | |
564 | * Raise exception. | |
565 | */ | |
566 | i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0); | |
567 | /*NOTREACHED*/ | |
568 | } | |
569 | ||
570 | /* | |
571 | * FPU error. Called by AST. | |
572 | */ | |
573 | ||
574 | void | |
575 | fpexterrflt(void) | |
576 | { | |
91447636 | 577 | register thread_t thr_act = current_thread(); |
1c79356b A |
578 | |
579 | ASSERT_IPL(SPL0); | |
1c79356b A |
580 | /* |
581 | * Save the FPU state and turn off the FPU. | |
582 | */ | |
583 | fp_save(thr_act); | |
1c79356b A |
584 | |
585 | /* | |
586 | * Raise FPU exception. | |
587 | * Locking not needed on pcb->ims.ifps, | |
588 | * since thread is running. | |
589 | */ | |
590 | i386_exception(EXC_ARITHMETIC, | |
591 | EXC_I386_EXTERR, | |
91447636 | 592 | thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status); |
1c79356b A |
593 | /*NOTREACHED*/ |
594 | } | |
595 | ||
596 | /* | |
597 | * Save FPU state. | |
598 | * | |
599 | * Locking not needed: | |
600 | * . if called from fpu_get_state, pcb already locked. | |
601 | * . if called from fpnoextflt or fp_intr, we are single-cpu | |
602 | * . otherwise, thread is running. | |
603 | */ | |
1c79356b A |
604 | void |
605 | fp_save( | |
91447636 | 606 | thread_t thr_act) |
1c79356b | 607 | { |
91447636 | 608 | register pcb_t pcb = thr_act->machine.pcb; |
1c79356b | 609 | register struct i386_fpsave_state *ifps = pcb->ims.ifps; |
1c79356b A |
610 | if (ifps != 0 && !ifps->fp_valid) { |
611 | /* registers are in FPU */ | |
612 | ifps->fp_valid = TRUE; | |
55e303ae A |
613 | ifps->fp_save_flavor = FP_387; |
614 | if (FXSAFE()) { | |
615 | fxsave(&ifps->fx_save_state); // save the SSE2/Fp state in addition is enabled | |
616 | ifps->fp_save_flavor = FP_FXSR; | |
617 | } | |
618 | fnsave(&ifps->fp_save_state); // also update the old save area for now... | |
1c79356b A |
619 | } |
620 | } | |
621 | ||
622 | /* | |
623 | * Restore FPU state from PCB. | |
624 | * | |
625 | * Locking not needed; always called on the current thread. | |
626 | */ | |
627 | ||
628 | void | |
629 | fp_load( | |
91447636 | 630 | thread_t thr_act) |
1c79356b | 631 | { |
91447636 | 632 | register pcb_t pcb = thr_act->machine.pcb; |
1c79356b A |
633 | register struct i386_fpsave_state *ifps; |
634 | ||
635 | ASSERT_IPL(SPL0); | |
636 | ifps = pcb->ims.ifps; | |
637 | if (ifps == 0) { | |
638 | ifps = (struct i386_fpsave_state *) zalloc(ifps_zone); | |
55e303ae | 639 | assert(ALIGNED(ifps,16)); |
1c79356b A |
640 | bzero((char *)ifps, sizeof *ifps); |
641 | pcb->ims.ifps = ifps; | |
642 | fpinit(); | |
643 | #if 1 | |
644 | /* | |
645 | * I'm not sure this is needed. Does the fpu regenerate the interrupt in | |
646 | * frstor or not? Without this code we may miss some exceptions, with it | |
647 | * we might send too many exceptions. | |
648 | */ | |
649 | } else if (ifps->fp_valid == 2) { | |
650 | /* delayed exception pending */ | |
651 | ||
652 | ifps->fp_valid = TRUE; | |
653 | clear_fpu(); | |
654 | /* | |
655 | * Raise FPU exception. | |
656 | * Locking not needed on pcb->ims.ifps, | |
657 | * since thread is running. | |
658 | */ | |
659 | i386_exception(EXC_ARITHMETIC, | |
660 | EXC_I386_EXTERR, | |
91447636 | 661 | thr_act->machine.pcb->ims.ifps->fp_save_state.fp_status); |
1c79356b A |
662 | /*NOTREACHED*/ |
663 | #endif | |
664 | } else { | |
55e303ae A |
665 | if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state); |
666 | else frstor(ifps->fp_save_state); | |
1c79356b A |
667 | } |
668 | ifps->fp_valid = FALSE; /* in FPU */ | |
669 | } | |
670 | ||
55e303ae | 671 | |
1c79356b A |
672 | /* |
673 | * Allocate and initialize FP state for current thread. | |
674 | * Don't load state. | |
675 | * | |
676 | * Locking not needed; always called on the current thread. | |
677 | */ | |
678 | void | |
679 | fp_state_alloc(void) | |
680 | { | |
91447636 | 681 | pcb_t pcb = current_thread()->machine.pcb; |
1c79356b A |
682 | struct i386_fpsave_state *ifps; |
683 | ||
684 | ifps = (struct i386_fpsave_state *)zalloc(ifps_zone); | |
55e303ae | 685 | assert(ALIGNED(ifps,16)); |
1c79356b A |
686 | bzero((char *)ifps, sizeof *ifps); |
687 | pcb->ims.ifps = ifps; | |
688 | ||
689 | ifps->fp_valid = TRUE; | |
690 | ifps->fp_save_state.fp_control = (0x037f | |
691 | & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC)) | |
692 | | (FPC_PC_53|FPC_IC_AFF); | |
693 | ifps->fp_save_state.fp_status = 0; | |
694 | ifps->fp_save_state.fp_tag = 0xffff; /* all empty */ | |
55e303ae A |
695 | ifps->fx_save_state.fx_control = ifps->fp_save_state.fp_control; |
696 | ifps->fx_save_state.fx_status = ifps->fp_save_state.fp_status; | |
697 | ifps->fx_save_state.fx_tag = 0x00; | |
698 | ifps->fx_save_state.fx_MXCSR = 0x1f80; | |
699 | ||
1c79356b A |
700 | } |
701 | ||
702 | ||
703 | /* | |
91447636 | 704 | * fpflush(thread_t) |
1c79356b A |
705 | * Flush the current act's state, if needed |
706 | * (used by thread_terminate_self to ensure fp faults | |
707 | * aren't satisfied by overly general trap code in the | |
708 | * context of the reaper thread) | |
709 | */ | |
710 | void | |
91447636 | 711 | fpflush(__unused thread_t thr_act) |
1c79356b | 712 | { |
1c79356b | 713 | /* not needed on MP x86s; fp not lazily evaluated */ |
1c79356b A |
714 | } |
715 | ||
716 | ||
717 | /* | |
718 | * Handle a coprocessor error interrupt on the AT386. | |
719 | * This comes in on line 5 of the slave PIC at SPL1. | |
720 | */ | |
721 | ||
722 | void | |
723 | fpintr(void) | |
724 | { | |
725 | spl_t s; | |
91447636 | 726 | thread_t thr_act = current_thread(); |
1c79356b A |
727 | |
728 | ASSERT_IPL(SPL1); | |
729 | /* | |
730 | * Turn off the extended 'busy' line. | |
731 | */ | |
732 | outb(0xf0, 0); | |
733 | ||
734 | /* | |
735 | * Save the FPU context to the thread using it. | |
736 | */ | |
1c79356b A |
737 | clear_ts(); |
738 | fp_save(thr_act); | |
739 | fninit(); | |
740 | clear_fpu(); | |
741 | ||
742 | /* | |
743 | * Since we are running on the interrupt stack, we must | |
744 | * signal the thread to take the exception when we return | |
745 | * to user mode. Use an AST to do this. | |
746 | * | |
747 | * Don`t set the thread`s AST field. If the thread is | |
748 | * descheduled before it takes the AST, it will notice | |
749 | * the FPU error when it reloads its FPU state. | |
750 | */ | |
751 | s = splsched(); | |
752 | mp_disable_preemption(); | |
753 | ast_on(AST_I386_FP); | |
754 | mp_enable_preemption(); | |
755 | splx(s); | |
756 | } |