|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: * Copyright (c) 1992, 1993 NeXT Computer, Inc.
27: *
28: * Floating point support.
29: *
30: * HISTORY
31: *
32: * 21 June 1993 ? at NeXT
33: * Major rewrite to add special support for SoftPC.
34: * 10 September 1992 ? at NeXT
35: * Created.
36: */
37:
38: #import <mach/mach_types.h>
39: #import <mach/exception.h>
40:
41: #import <machdep/i386/cpu_inline.h>
42: #import <machdep/i386/fp_inline.h>
43: #import <machdep/i386/fp_exported.h>
44: #import <machdep/i386/configure.h>
45:
46: #import <fp_emul.h>
47: #if FP_EMUL
48: #import <machdep/i386/table_inline.h>
49: #import <machdep/i386/desc_inline.h>
50: #endif
51:
52: #import <pc_support.h>
53: #if PC_SUPPORT
54: #import <machdep/i386/pc_support/PCprivate.h>
55: #endif
56:
57: static thread_t fp_thread;
58:
59: static
60: inline void fp_init(fp_state_t *fpstate),
61: fp_init_default(fp_state_t *fpstate),
62: fp_unowned(void),
63: fp_save(void),
64: fp_restore(void);
65:
66: static void fp_switch(void);
67:
68: #import <bsd/i386/reboot.h>
69: extern int boothowto;
70:
71: /*
72: * Determine whether this machine
73: * contains a hardware FPU and configure
74: * the CPU accordingly.
75: */
76:
77: void
78: fp_configure(void)
79: {
80: cr0_t _cr0 = cr0();
81: volatile
82: unsigned short status_word;
83:
84: /*
85: * Make sure that floating
86: * point operations do not
87: * cause a trap.
88: */
89: _cr0.em = _cr0.ts = 0;
90: set_cr0(_cr0);
91:
92: /*
93: * Machines with a hardware
94: * FPU will return a status
95: * word of zero after an finit,
96: * Machines without an FPU will
97: * not modify the destination
98: * operand.
99: */
100: status_word = 0x5a5a;
101:
102: asm volatile(
103: "fninit;
104: fnstsw %0"
105: : "=m" (status_word));
106:
107: if (status_word != 0 || (boothowto&RB_NOFP) != 0) {
108: /*
109: * Either this machine
110: * does not have an FPU,
111: * or we want to test the
112: * floating point emulator.
113: * Turn on the EM bit in CR0
114: * so that all floating point
115: * opcodes cause a trap.
116: */
117: _cr0.em = 1; // cause floating point instructions to trap INT7
118: _cr0.mp = 0; // cause WAIT instructions to trap INT7
119: set_cr0(_cr0);
120: #if FP_EMUL
121: cpu_config.fpu_type = FPU_EMUL;
122: #endif
123: }
124: else {
125: _cr0.ne = 1; // cause unmasked floating point exceptions to trap INT16
126: _cr0.mp = 1;
127: set_cr0(_cr0);
128: cpu_config.fpu_type = FPU_HDW;
129: }
130: }
131:
132: /*
133: * Called on occurance of INT7 from
134: * user mode.
135: */
136:
137: void
138: fp_noextension(
139: thread_saved_state_t *state
140: )
141: {
142: if (cpu_config.fpu_type == FPU_NONE) {
143: /*
144: * If we are not providing
145: * floating point support,
146: * just send an exception.
147: */
148: exception(EXC_EMULATION, EXC_I386_NOEXTENSION, 0);
149: /* NOTREACHED */
150: }
151:
152: /*
153: * Switch the fpu context
154: * if necessary.
155: */
156: fp_switch();
157:
158: #if FP_EMUL
159: if (cpu_config.fpu_type == FPU_EMUL) {
160: /*
161: * If we are using the
162: * kernel floating point
163: * emulator, call it.
164: */
165: e80387(state);
166: }
167: #endif
168: }
169:
170: /*
171: * Called on occurance of INT16 from
172: * user mode.
173: */
174:
175: void
176: fp_extension_fault(
177: thread_saved_state_t *state
178: )
179: {
180: thread_t exception_thread = fp_thread;
181:
182: /*
183: * Stop the floating point unit
184: * dead in its tracks. We do not
185: * want another exception to occur.
186: */
187: fp_save(); // sets fp_thread = THREAD_NULL
188:
189: if (exception_thread == current_thread()) {
190: exception(EXC_ARITHMETIC, EXC_I386_EXTENSION_FAULT, 0);
191: /* NOTREACHED */
192: }
193: else
194: thread_ast_set(exception_thread, AST_FP_EXTEN);
195: }
196:
197: /*
198: * Called on occurance of INT16 from
199: * kernel mode.
200: */
201: void
202: fp_kernel_extension_fault(
203: thread_saved_state_t *state
204: )
205: {
206: thread_t exception_thread = fp_thread;
207:
208: /*
209: * Stop the floating point unit.
210: */
211: fp_save(); // sets fp_thread = THREAD_NULL
212:
213: thread_ast_set(exception_thread, AST_FP_EXTEN);
214: if (exception_thread == current_thread())
215: ast_propagate(exception_thread, cpu_number());
216: }
217:
218: void
219: fp_ast(
220: thread_t thread
221: )
222: {
223: thread_ast_clear(thread, AST_FP_EXTEN);
224:
225: exception(EXC_ARITHMETIC, EXC_I386_EXTENSION_FAULT, 0);
226: /* NOTREACHED */
227: }
228:
229: /*
230: * Initialize a saved floating point
231: * context as if an FINIT was performed.
232: * Used to initialize a context for the
233: * software emulator, as well as to
234: * initialize an unused context so that
235: * thread_get_state() returns something
236: * sensible for a thread that has not
237: * performed any floating point operations.
238: */
239:
240: /*
241: * Initialize the floating point
242: * state to the hardware default.
243: */
244: static inline
245: void
246: fp_init_default(
247: fp_state_t *fpstate
248: )
249: {
250: *(unsigned short *)&fpstate->environ.control = 0x037F;
251: *(unsigned short *)&fpstate->environ.status = 0x0000;
252: *(unsigned short *)&fpstate->environ.tag = 0xFFFF;
253:
254: fpstate->environ.ip = 0x00000000;
255: fpstate->environ.opcode = 0x0000;
256: fpstate->environ.cs = NULL_SEL;
257: fpstate->environ.dp = 0x00000000;
258: fpstate->environ.ds = NULL_SEL;
259: }
260:
261: /*
262: * Initialize the floating point
263: * state to that needed by the
264: * NEXTSTEP floating point model:
265: * double precision, round to
266: * nearest or even.
267: */
268: static inline
269: void
270: fp_init(
271: fp_state_t *fpstate
272: )
273: {
274: *(unsigned short *)&fpstate->environ.control = 0x027F;
275: *(unsigned short *)&fpstate->environ.status = 0x0000;
276: *(unsigned short *)&fpstate->environ.tag = 0xFFFF;
277:
278: fpstate->environ.ip = 0x00000000;
279: fpstate->environ.opcode = 0x0000;
280: fpstate->environ.cs = NULL_SEL;
281: fpstate->environ.dp = 0x00000000;
282: fpstate->environ.ds = NULL_SEL;
283: }
284:
285: /*
286: * Save the floating point context
287: * for the thread which owns the FPU
288: * and mark it unowned. N.B. This might
289: * not be the current thread.
290: */
291:
292: static inline
293: void
294: fp_save(void)
295: {
296: thread_t thread = fp_thread;
297:
298: if (thread) {
299: #if PC_SUPPORT
300: PCshared_t shared = threadPCShared(thread);
301:
302: if (shared && shared->fpuOwned) {
303: if (cpu_config.fpu_type == FPU_HDW)
304: fnsave(&shared->fpuState); // clears TS flag
305:
306: shared->fpuStateValid = TRUE;
307: }
308: else {
309: #endif
310: if (cpu_config.fpu_type == FPU_HDW)
311: fnsave(&thread->pcb->fpstate); // clears TS flag
312:
313: thread->pcb->fpvalid = TRUE;
314: #if PC_SUPPORT
315: }
316: #endif
317:
318: fp_unowned();
319: }
320: }
321:
322: /*
323: * Restore the floating point context of the
324: * current thread. The main assumption here
325: * is that the FPU is currently NOT owned by
326: * any thread.
327: */
328:
329: static inline
330: void
331: fp_restore(void)
332: {
333: thread_t thread = current_thread();
334: #if PC_SUPPORT
335: PCshared_t shared = threadPCShared(thread);
336:
337: if (shared) {
338: PCcontext_t context = currentContext(shared);
339:
340: if (context->running) {
341: if (shared->fpuStateValid) {
342: if (cpu_config.fpu_type == FPU_HDW)
343: frstor(&shared->fpuState); // clears TS flag
344:
345: shared->fpuStateValid = FALSE;
346: }
347: else {
348: fp_init_default(&shared->fpuState);
349:
350: if (cpu_config.fpu_type == FPU_HDW)
351: frstor(&shared->fpuState); // clears TS flag
352: }
353:
354: #if FP_EMUL
355: if (cpu_config.fpu_type == FPU_EMUL)
356: map_data(sel_to_gdt_entry(FPSTATE_SEL),
357: (vm_offset_t) &shared->fpuState
358: + KERNEL_LINEAR_BASE,
359: (vm_size_t) sizeof (shared->fpuState),
360: KERN_PRIV, FALSE);
361: #endif
362:
363: shared->fpuOwned = TRUE;
364: }
365: else {
366: shared->fpuOwned = FALSE;
367: goto restore_thread;
368: }
369: }
370: else {
371: restore_thread:
372: #endif
373: if (thread->pcb->fpvalid) {
374: if (cpu_config.fpu_type == FPU_HDW)
375: frstor(&thread->pcb->fpstate); // clears TS flag
376:
377: thread->pcb->fpvalid = FALSE;
378: }
379: else {
380: fp_init(&thread->pcb->fpstate);
381:
382: if (cpu_config.fpu_type == FPU_HDW)
383: frstor(&thread->pcb->fpstate); // clears TS flag
384: }
385:
386: #if FP_EMUL
387: if (cpu_config.fpu_type == FPU_EMUL)
388: map_data(sel_to_gdt_entry(FPSTATE_SEL),
389: (vm_offset_t) &thread->pcb->fpstate
390: + KERNEL_LINEAR_BASE,
391: (vm_size_t) sizeof (thread->pcb->fpstate),
392: KERN_PRIV, FALSE);
393: #endif
394: #if PC_SUPPORT
395: }
396: #endif
397: }
398:
399: /*
400: * Restore the floating point context
401: * of the current thread, saving the
402: * current context if necessary.
403: */
404:
405: static
406: void
407: fp_switch(void)
408: {
409: thread_t thread = current_thread();
410:
411: if (thread == fp_thread) {
412: #if PC_SUPPORT
413: PCshared_t shared = threadPCShared(thread);
414:
415: if (shared) {
416: PCcontext_t context = currentContext(shared);
417:
418: if (context->running) {
419: if (!shared->fpuOwned) {
420: if (cpu_config.fpu_type == FPU_HDW)
421: fnsave(&thread->pcb->fpstate); // clears TS flag
422:
423: thread->pcb->fpvalid = TRUE;
424:
425: if (shared->fpuStateValid) {
426: if (cpu_config.fpu_type == FPU_HDW)
427: frstor(&shared->fpuState); // clears TS flag
428:
429: shared->fpuStateValid = FALSE;
430: }
431: else {
432: fp_init_default(&shared->fpuState);
433:
434: if (cpu_config.fpu_type == FPU_HDW)
435: frstor(&shared->fpuState); // clears TS flag
436: }
437:
438: #if FP_EMUL
439: if (cpu_config.fpu_type == FPU_EMUL)
440: map_data(sel_to_gdt_entry(FPSTATE_SEL),
441: (vm_offset_t) &shared->fpuState
442: + KERNEL_LINEAR_BASE,
443: (vm_size_t) sizeof (shared->fpuState),
444: KERN_PRIV, FALSE);
445: #endif
446:
447: shared->fpuOwned = TRUE;
448: }
449: }
450: else {
451: if (shared->fpuOwned) {
452: if (cpu_config.fpu_type == FPU_HDW)
453: fnsave(&shared->fpuState); // clears TS flag
454:
455: shared->fpuStateValid = TRUE;
456:
457: if (thread->pcb->fpvalid) {
458: if (cpu_config.fpu_type == FPU_HDW)
459: frstor(&thread->pcb->fpstate);// clears TS flag
460:
461: thread->pcb->fpvalid = FALSE;
462: }
463: else {
464: fp_init(&thread->pcb->fpstate);
465:
466: if (cpu_config.fpu_type == FPU_HDW)
467: frstor(&thread->pcb->fpstate);// clears TS flag
468: }
469:
470: #if FP_EMUL
471: if (cpu_config.fpu_type == FPU_EMUL)
472: map_data(sel_to_gdt_entry(FPSTATE_SEL),
473: (vm_offset_t) &thread->pcb->fpstate
474: + KERNEL_LINEAR_BASE,
475: (vm_size_t)
476: sizeof (thread->pcb->fpstate),
477: KERN_PRIV, FALSE);
478: #endif
479:
480: shared->fpuOwned = FALSE;
481: }
482: }
483: }
484: #endif
485: }
486: else {
487: fp_save(); fp_thread = thread; fp_restore();
488: }
489:
490: clts(); // make sure that the TS flag is cleared
491: }
492:
493: /*
494: * Mark the FPU as currently
495: * unowned.
496: */
497:
498: static inline
499: void
500: fp_unowned(void)
501: {
502: fp_thread = THREAD_NULL;
503: setts();
504: }
505:
506: /*
507: * Mark the FPU unowned if the
508: * thread currently owns it. The
509: * context is discarded.
510: */
511:
512: void
513: fp_terminate(
514: thread_t thread
515: )
516: {
517: if (thread == fp_thread)
518: fp_unowned();
519: }
520:
521: /*
522: * Update the saved floating point
523: * context of the thread if necessary.
524: * Perform a software FINIT if the
525: * thread has not performed any floating
526: * point operations yet.
527: */
528:
529: void
530: fp_synch(
531: thread_t thread
532: )
533: {
534: if (thread == fp_thread)
535: fp_save();
536: else {
537: if (!thread->pcb->fpvalid)
538: fp_init(&thread->pcb->fpstate);
539: }
540: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.