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