|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
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.
11: *
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
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
19: *
20: * @APPLE_LICENSE_HEADER_END@
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 [email protected]
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:
53: #include <cpus.h>
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>
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>
70: #include <i386/pio.h>
71: #include <i386/misc_protos.h>
72:
73: #if 0
74: #include <i386/ipl.h>
75: extern int curr_ipl;
76: #define ASSERT_IPL(L) \
77: { \
78: if (curr_ipl != L) { \
79: printf("IPL is %d, expected %d\n", curr_ipl, L); \
80: panic("fpu: wrong ipl"); \
81: } \
82: }
83: #else
84: #define ASSERT_IPL(L)
85: #endif
86:
87: int fp_kind = FP_387; /* 80387 present */
88: zone_t ifps_zone; /* zone for FPU save area */
89:
90: #if NCPUS == 1
91: volatile thread_act_t fp_act = THR_ACT_NULL;
92: /* thread whose state is in FPU */
93: /* always THR_ACT_NULL if emulating FPU */
94: volatile thread_act_t fp_intr_act = THR_ACT_NULL;
95:
96:
97: #define clear_fpu() \
98: { \
99: set_ts(); \
100: fp_act = THR_ACT_NULL; \
101: }
102:
103: #else /* NCPUS > 1 */
104: #define clear_fpu() \
105: { \
106: set_ts(); \
107: }
108:
109: #endif
110:
111: /* Forward */
112:
113: extern void fpinit(void);
114: extern void fp_save(
115: thread_act_t thr_act);
116: extern void fp_load(
117: thread_act_t thr_act);
118:
119: /*
120: * Look for FPU and initialize it.
121: * Called on each CPU.
122: */
123: void
124: init_fpu(void)
125: {
126: unsigned short status, control;
127:
128: /*
129: * Check for FPU by initializing it,
130: * then trying to read the correct bit patterns from
131: * the control and status registers.
132: */
133: set_cr0(get_cr0() & ~(CR0_EM|CR0_TS)); /* allow use of FPU */
134:
135: fninit();
136: status = fnstsw();
137: fnstcw(&control);
138:
139: if ((status & 0xff) == 0 &&
140: (control & 0x103f) == 0x3f)
141: {
142: #if 0
143: /*
144: * We have a FPU of some sort.
145: * Compare -infinity against +infinity
146: * to check whether we have a 287 or a 387.
147: */
148: volatile double fp_infinity, fp_one, fp_zero;
149: fp_one = 1.0;
150: fp_zero = 0.0;
151: fp_infinity = fp_one / fp_zero;
152: if (fp_infinity == -fp_infinity) {
153: /*
154: * We have an 80287.
155: */
156: fp_kind = FP_287;
157: __asm__ volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */
158: }
159: else
160: #endif
161: {
162: /*
163: * We have a 387.
164: */
165: fp_kind = FP_387;
166: }
167: /*
168: * Trap wait instructions. Turn off FPU for now.
169: */
170: set_cr0(get_cr0() | CR0_TS | CR0_MP);
171: }
172: else
173: {
174: /*
175: * NO FPU.
176: */
177: fp_kind = FP_NO;
178: set_cr0(get_cr0() | CR0_EM);
179: }
180: }
181:
182: /*
183: * Initialize FP handling.
184: */
185: void
186: fpu_module_init(void)
187: {
188: ifps_zone = zinit(sizeof(struct i386_fpsave_state),
189: THREAD_MAX * sizeof(struct i386_fpsave_state),
190: THREAD_CHUNK * sizeof(struct i386_fpsave_state),
191: "i386 fpsave state");
192: }
193:
194: /*
195: * Free a FPU save area.
196: * Called only when thread terminating - no locking necessary.
197: */
198: void
199: fp_free(fps)
200: struct i386_fpsave_state *fps;
201: {
202: ASSERT_IPL(SPL0);
203: #if NCPUS == 1
204: if ((fp_act != THR_ACT_NULL) && (fp_act->mact.pcb->ims.ifps == fps)) {
205: /*
206: * Make sure we don't get FPU interrupts later for
207: * this thread
208: */
209: fwait();
210:
211: /* Mark it free and disable access */
212: clear_fpu();
213: }
214: #endif /* NCPUS == 1 */
215: zfree(ifps_zone, (vm_offset_t) fps);
216: }
217:
218: /*
219: * Set the floating-point state for a thread.
220: * If the thread is not the current thread, it is
221: * not running (held). Locking needed against
222: * concurrent fpu_set_state or fpu_get_state.
223: */
224: kern_return_t
225: fpu_set_state(
226: thread_act_t thr_act,
227: struct i386_float_state *state)
228: {
229: register pcb_t pcb;
230: register struct i386_fpsave_state *ifps;
231: register struct i386_fpsave_state *new_ifps;
232:
233: ASSERT_IPL(SPL0);
234: if (fp_kind == FP_NO)
235: return KERN_FAILURE;
236:
237: assert(thr_act != THR_ACT_NULL);
238: pcb = thr_act->mact.pcb;
239:
240: #if NCPUS == 1
241:
242: /*
243: * If this thread`s state is in the FPU,
244: * discard it; we are replacing the entire
245: * FPU state.
246: */
247: if (fp_act == thr_act) {
248: fwait(); /* wait for possible interrupt */
249: clear_fpu(); /* no state in FPU */
250: }
251: #endif
252:
253: if (state->initialized == 0) {
254: /*
255: * new FPU state is 'invalid'.
256: * Deallocate the fp state if it exists.
257: */
258: simple_lock(&pcb->lock);
259: ifps = pcb->ims.ifps;
260: pcb->ims.ifps = 0;
261: simple_unlock(&pcb->lock);
262:
263: if (ifps != 0) {
264: zfree(ifps_zone, (vm_offset_t) ifps);
265: }
266: }
267: else {
268: /*
269: * Valid state. Allocate the fp state if there is none.
270: */
271: register struct i386_fp_save *user_fp_state;
272: register struct i386_fp_regs *user_fp_regs;
273:
274: user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
275: user_fp_regs = (struct i386_fp_regs *)
276: &state->hw_state[sizeof(struct i386_fp_save)];
277:
278: new_ifps = 0;
279: Retry:
280: simple_lock(&pcb->lock);
281: ifps = pcb->ims.ifps;
282: if (ifps == 0) {
283: if (new_ifps == 0) {
284: simple_unlock(&pcb->lock);
285: new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
286: goto Retry;
287: }
288: ifps = new_ifps;
289: new_ifps = 0;
290: pcb->ims.ifps = ifps;
291: }
292:
293: /*
294: * Ensure that reserved parts of the environment are 0.
295: */
296: bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
297:
298: ifps->fp_save_state.fp_control = user_fp_state->fp_control;
299: ifps->fp_save_state.fp_status = user_fp_state->fp_status;
300: ifps->fp_save_state.fp_tag = user_fp_state->fp_tag;
301: ifps->fp_save_state.fp_eip = user_fp_state->fp_eip;
302: ifps->fp_save_state.fp_cs = user_fp_state->fp_cs;
303: ifps->fp_save_state.fp_opcode = user_fp_state->fp_opcode;
304: ifps->fp_save_state.fp_dp = user_fp_state->fp_dp;
305: ifps->fp_save_state.fp_ds = user_fp_state->fp_ds;
306: ifps->fp_regs = *user_fp_regs;
307:
308: simple_unlock(&pcb->lock);
309: if (new_ifps != 0)
310: zfree(ifps_zone, (vm_offset_t) ifps);
311: }
312:
313: return KERN_SUCCESS;
314: }
315:
316: /*
317: * Get 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_get_state(
324: thread_act_t thr_act,
325: register struct i386_float_state *state)
326: {
327: register pcb_t pcb;
328: register struct i386_fpsave_state *ifps;
329:
330: ASSERT_IPL(SPL0);
331: if (fp_kind == FP_NO)
332: return KERN_FAILURE;
333:
334: assert(thr_act != THR_ACT_NULL);
335: pcb = thr_act->mact.pcb;
336:
337: simple_lock(&pcb->lock);
338: ifps = pcb->ims.ifps;
339: if (ifps == 0) {
340: /*
341: * No valid floating-point state.
342: */
343: simple_unlock(&pcb->lock);
344: bzero((char *)state, sizeof(struct i386_float_state));
345: return KERN_SUCCESS;
346: }
347:
348: /* Make sure we`ve got the latest fp state info */
349: /* If the live fpu state belongs to our target */
350: #if NCPUS == 1
351: if (thr_act == fp_act)
352: #else
353: if (thr_act == current_act())
354: #endif
355: {
356: clear_ts();
357: fp_save(thr_act);
358: clear_fpu();
359: }
360:
361: state->fpkind = fp_kind;
362: state->exc_status = 0;
363:
364: {
365: register struct i386_fp_save *user_fp_state;
366: register struct i386_fp_regs *user_fp_regs;
367:
368: state->initialized = ifps->fp_valid;
369:
370: user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
371: user_fp_regs = (struct i386_fp_regs *)
372: &state->hw_state[sizeof(struct i386_fp_save)];
373:
374: /*
375: * Ensure that reserved parts of the environment are 0.
376: */
377: bzero((char *)user_fp_state, sizeof(struct i386_fp_save));
378:
379: user_fp_state->fp_control = ifps->fp_save_state.fp_control;
380: user_fp_state->fp_status = ifps->fp_save_state.fp_status;
381: user_fp_state->fp_tag = ifps->fp_save_state.fp_tag;
382: user_fp_state->fp_eip = ifps->fp_save_state.fp_eip;
383: user_fp_state->fp_cs = ifps->fp_save_state.fp_cs;
384: user_fp_state->fp_opcode = ifps->fp_save_state.fp_opcode;
385: user_fp_state->fp_dp = ifps->fp_save_state.fp_dp;
386: user_fp_state->fp_ds = ifps->fp_save_state.fp_ds;
387: *user_fp_regs = ifps->fp_regs;
388: }
389: simple_unlock(&pcb->lock);
390:
391: return KERN_SUCCESS;
392: }
393:
394: /*
395: * Initialize FPU.
396: *
397: * Raise exceptions for:
398: * invalid operation
399: * divide by zero
400: * overflow
401: *
402: * Use 53-bit precision.
403: */
404: void
405: fpinit(void)
406: {
407: unsigned short control;
408:
409: ASSERT_IPL(SPL0);
410: clear_ts();
411: fninit();
412: fnstcw(&control);
413: control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
414: control |= (FPC_PC_53 | /* Set precision */
415: FPC_RC_RN | /* round-to-nearest */
416: FPC_ZE | /* Suppress zero-divide */
417: FPC_OE | /* and overflow */
418: FPC_UE | /* underflow */
419: FPC_IE | /* Allow NaNQs and +-INF */
420: FPC_DE | /* Allow denorms as operands */
421: FPC_PE); /* No trap for precision loss */
422: fldcw(control);
423: }
424:
425: /*
426: * Coprocessor not present.
427: */
428:
429: void
430: fpnoextflt(void)
431: {
432: /*
433: * Enable FPU use.
434: */
435: ASSERT_IPL(SPL0);
436: clear_ts();
437: #if NCPUS == 1
438:
439: /*
440: * If this thread`s state is in the FPU, we are done.
441: */
442: if (fp_act == current_act())
443: return;
444:
445: /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
446: * here if the current fpu instruction generates an error.
447: */
448: fwait();
449: /*
450: * If another thread`s state is in the FPU, save it.
451: */
452: if (fp_act != THR_ACT_NULL) {
453: fp_save(fp_act);
454: }
455:
456: /*
457: * Give this thread the FPU.
458: */
459: fp_act = current_act();
460:
461: #endif /* NCPUS == 1 */
462:
463: /*
464: * Load this thread`s state into the FPU.
465: */
466: fp_load(current_act());
467: }
468:
469: /*
470: * FPU overran end of segment.
471: * Re-initialize FPU. Floating point state is not valid.
472: */
473:
474: void
475: fpextovrflt(void)
476: {
477: register thread_act_t thr_act = current_act();
478: register pcb_t pcb;
479: register struct i386_fpsave_state *ifps;
480:
481: #if NCPUS == 1
482:
483: /*
484: * Is exception for the currently running thread?
485: */
486: if (fp_act != thr_act) {
487: /* Uh oh... */
488: panic("fpextovrflt");
489: }
490: #endif
491:
492: /*
493: * This is a non-recoverable error.
494: * Invalidate the thread`s FPU state.
495: */
496: pcb = thr_act->mact.pcb;
497: simple_lock(&pcb->lock);
498: ifps = pcb->ims.ifps;
499: pcb->ims.ifps = 0;
500: simple_unlock(&pcb->lock);
501:
502: /*
503: * Re-initialize the FPU.
504: */
505: clear_ts();
506: fninit();
507:
508: /*
509: * And disable access.
510: */
511: clear_fpu();
512:
513: if (ifps)
514: zfree(ifps_zone, (vm_offset_t) ifps);
515:
516: /*
517: * Raise exception.
518: */
519: i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
520: /*NOTREACHED*/
521: }
522:
523: /*
524: * FPU error. Called by AST.
525: */
526:
527: void
528: fpexterrflt(void)
529: {
530: register thread_act_t thr_act = current_act();
531:
532: ASSERT_IPL(SPL0);
533: #if NCPUS == 1
534: /*
535: * Since FPU errors only occur on ESC or WAIT instructions,
536: * the current thread should own the FPU. If it didn`t,
537: * we should have gotten the task-switched interrupt first.
538: */
539: if (fp_act != THR_ACT_NULL) {
540: panic("fpexterrflt");
541: return;
542: }
543:
544: /*
545: * Check if we got a context switch between the interrupt and the AST
546: * This can happen if the interrupt arrived after the FPU AST was
547: * checked. In this case, raise the exception in fp_load when this
548: * thread next time uses the FPU. Remember exception condition in
549: * fp_valid (extended boolean 2).
550: */
551: if (fp_intr_act != thr_act) {
552: if (fp_intr_act == THR_ACT_NULL) {
553: panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
554: return;
555: }
556: fp_intr_act->mact.pcb->ims.ifps->fp_valid = 2;
557: fp_intr_act = THR_ACT_NULL;
558: return;
559: }
560: fp_intr_act = THR_ACT_NULL;
561: #else /* NCPUS == 1 */
562: /*
563: * Save the FPU state and turn off the FPU.
564: */
565: fp_save(thr_act);
566: #endif /* NCPUS == 1 */
567:
568: /*
569: * Raise FPU exception.
570: * Locking not needed on pcb->ims.ifps,
571: * since thread is running.
572: */
573: i386_exception(EXC_ARITHMETIC,
574: EXC_I386_EXTERR,
575: thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
576: /*NOTREACHED*/
577: }
578:
579: /*
580: * Save FPU state.
581: *
582: * Locking not needed:
583: * . if called from fpu_get_state, pcb already locked.
584: * . if called from fpnoextflt or fp_intr, we are single-cpu
585: * . otherwise, thread is running.
586: */
587:
588: void
589: fp_save(
590: thread_act_t thr_act)
591: {
592: register pcb_t pcb = thr_act->mact.pcb;
593: register struct i386_fpsave_state *ifps = pcb->ims.ifps;
594:
595: if (ifps != 0 && !ifps->fp_valid) {
596: /* registers are in FPU */
597: ifps->fp_valid = TRUE;
598: fnsave(&ifps->fp_save_state);
599: }
600: }
601:
602: /*
603: * Restore FPU state from PCB.
604: *
605: * Locking not needed; always called on the current thread.
606: */
607:
608: void
609: fp_load(
610: thread_act_t thr_act)
611: {
612: register pcb_t pcb = thr_act->mact.pcb;
613: register struct i386_fpsave_state *ifps;
614:
615: ASSERT_IPL(SPL0);
616: ifps = pcb->ims.ifps;
617: if (ifps == 0) {
618: ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
619: bzero((char *)ifps, sizeof *ifps);
620: pcb->ims.ifps = ifps;
621: fpinit();
622: #if 1
623: /*
624: * I'm not sure this is needed. Does the fpu regenerate the interrupt in
625: * frstor or not? Without this code we may miss some exceptions, with it
626: * we might send too many exceptions.
627: */
628: } else if (ifps->fp_valid == 2) {
629: /* delayed exception pending */
630:
631: ifps->fp_valid = TRUE;
632: clear_fpu();
633: /*
634: * Raise FPU exception.
635: * Locking not needed on pcb->ims.ifps,
636: * since thread is running.
637: */
638: i386_exception(EXC_ARITHMETIC,
639: EXC_I386_EXTERR,
640: thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
641: /*NOTREACHED*/
642: #endif
643: } else {
644: frstor(ifps->fp_save_state);
645: }
646: ifps->fp_valid = FALSE; /* in FPU */
647: }
648:
649: /*
650: * Allocate and initialize FP state for current thread.
651: * Don't load state.
652: *
653: * Locking not needed; always called on the current thread.
654: */
655: void
656: fp_state_alloc(void)
657: {
658: pcb_t pcb = current_act()->mact.pcb;
659: struct i386_fpsave_state *ifps;
660:
661: ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
662: bzero((char *)ifps, sizeof *ifps);
663: pcb->ims.ifps = ifps;
664:
665: ifps->fp_valid = TRUE;
666: ifps->fp_save_state.fp_control = (0x037f
667: & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
668: | (FPC_PC_53|FPC_IC_AFF);
669: ifps->fp_save_state.fp_status = 0;
670: ifps->fp_save_state.fp_tag = 0xffff; /* all empty */
671: }
672:
673:
674: /*
675: * fpflush(thread_act_t)
676: * Flush the current act's state, if needed
677: * (used by thread_terminate_self to ensure fp faults
678: * aren't satisfied by overly general trap code in the
679: * context of the reaper thread)
680: */
681: void
682: fpflush(thread_act_t thr_act)
683: {
684: #if NCPUS == 1
685: if (fp_act && thr_act == fp_act) {
686: clear_ts();
687: fwait();
688: clear_fpu();
689: }
690: #else
691: /* not needed on MP x86s; fp not lazily evaluated */
692: #endif
693: }
694:
695:
696: /*
697: * Handle a coprocessor error interrupt on the AT386.
698: * This comes in on line 5 of the slave PIC at SPL1.
699: */
700:
701: void
702: fpintr(void)
703: {
704: spl_t s;
705: thread_act_t thr_act = current_act();
706:
707: ASSERT_IPL(SPL1);
708: /*
709: * Turn off the extended 'busy' line.
710: */
711: outb(0xf0, 0);
712:
713: /*
714: * Save the FPU context to the thread using it.
715: */
716: #if NCPUS == 1
717: if (fp_act == THR_ACT_NULL) {
718: printf("fpintr: FPU not belonging to anyone!\n");
719: clear_ts();
720: fninit();
721: clear_fpu();
722: return;
723: }
724:
725: if (fp_act != thr_act) {
726: /*
727: * FPU exception is for a different thread.
728: * When that thread again uses the FPU an exception will be
729: * raised in fp_load. Remember the condition in fp_valid (== 2).
730: */
731: clear_ts();
732: fp_save(fp_act);
733: fp_act->mact.pcb->ims.ifps->fp_valid = 2;
734: fninit();
735: clear_fpu();
736: /* leave fp_intr_act THR_ACT_NULL */
737: return;
738: }
739: if (fp_intr_act != THR_ACT_NULL)
740: panic("fp_intr: already caught intr");
741: fp_intr_act = thr_act;
742: #endif /* NCPUS == 1 */
743:
744: clear_ts();
745: fp_save(thr_act);
746: fninit();
747: clear_fpu();
748:
749: /*
750: * Since we are running on the interrupt stack, we must
751: * signal the thread to take the exception when we return
752: * to user mode. Use an AST to do this.
753: *
754: * Don`t set the thread`s AST field. If the thread is
755: * descheduled before it takes the AST, it will notice
756: * the FPU error when it reloads its FPU state.
757: */
758: s = splsched();
759: mp_disable_preemption();
760: ast_on(AST_I386_FP);
761: mp_enable_preemption();
762: splx(s);
763: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.