Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / sys / platform / pc32 / gnu / fpemul / fpu_entry.c
1 /*
2  *  fpu_entry.c
3  *
4  * The entry function for wm-FPU-emu
5  *
6  *
7  * Copyright (C) 1992,1993,1994
8  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
9  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
10  * All rights reserved.
11  *
12  * This copyright notice covers the redistribution and use of the
13  * FPU emulator developed by W. Metzenthen. It covers only its use
14  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
15  * use is not permitted under this copyright.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must include information specifying
23  *    that source code for the emulator is freely available and include
24  *    either:
25  *      a) an offer to provide the source code for a nominal distribution
26  *         fee, or
27  *      b) list at least two alternative methods whereby the source
28  *         can be obtained, e.g. a publically accessible bulletin board
29  *         and an anonymous ftp site from which the software can be
30  *         downloaded.
31  * 3. All advertising materials specifically mentioning features or use of
32  *    this emulator must acknowledge that it was developed by W. Metzenthen.
33  * 4. The name of W. Metzenthen may not be used to endorse or promote
34  *    products derived from this software without specific prior written
35  *    permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
40  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
41  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
42  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
43  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
44  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
45  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
46  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  *
49  * The purpose of this copyright, based upon the Berkeley copyright, is to
50  * ensure that the covered software remains freely available to everyone.
51  *
52  * The software (with necessary differences) is also available, but under
53  * the terms of the GNU copyleft, for the Linux operating system and for
54  * the djgpp ms-dos extender.
55  *
56  * W. Metzenthen   June 1994.
57  *
58  * $FreeBSD: src/sys/gnu/i386/fpemul/fpu_entry.c,v 1.23 1999/10/12 02:23:14 msmith Exp $
59  * $DragonFly: src/sys/platform/pc32/gnu/fpemul/Attic/fpu_entry.c,v 1.2 2003/06/17 04:28:34 dillon Exp $
60  *
61  */
62
63 /*---------------------------------------------------------------------------+
64  | Note:                                                                     |
65  |    The file contains code which accesses user memory.                     |
66  |    Emulator static data may change when user memory is accessed, due to   |
67  |    other processes using the emulator while swapping is in progress.      |
68  +---------------------------------------------------------------------------*/
69
70 /*---------------------------------------------------------------------------+
71  | math_emulate() is the sole entry point for wm-FPU-emu                     |
72  +---------------------------------------------------------------------------*/
73
74
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/proc.h>
78 #include <sys/kernel.h>
79
80 #include <machine/cpu.h>
81 #include <machine/pcb.h>
82
83 #include <gnu/i386/fpemul/fpu_emu.h>
84 #include <gnu/i386/fpemul/fpu_system.h>
85 #include <gnu/i386/fpemul/exception.h>
86 #include <gnu/i386/fpemul/status_w.h>
87
88
89 #define __BAD__ Un_impl         /* Not implemented */
90
91 #define FPU_LOOKAHEAD   1       /* For performance boost */
92
93 #if FPU_LOOKAHEAD != 0          /* I think thet we have to limit the */
94 #define LOOKAHEAD_LIMIT 7       /* Max number of lookahead instructions*/
95 #endif                          /* Or else a prog consisting of a million */
96                                 /* fnops will spend all its time in kernel*/
97
98 #ifndef NO_UNDOC_CODE           /* Un-documented FPU op-codes supported by
99                                  * default. */
100
101 /* WARNING: These codes are not documented by Intel in their 80486 manual
102    and may not work on FPU clones or later Intel FPUs. */
103
104 /* Changes to support the un-doc codes provided by Linus Torvalds. */
105
106 #define _d9_d8_ fstp_i          /* unofficial code (19) */
107 #define _dc_d0_ fcom_st         /* unofficial code (14) */
108 #define _dc_d8_ fcompst         /* unofficial code (1c) */
109 #define _dd_c8_ fxch_i          /* unofficial code (0d) */
110 #define _de_d0_ fcompst         /* unofficial code (16) */
111 #define _df_c0_ ffreep          /* unofficial code (07) ffree + pop */
112 #define _df_c8_ fxch_i          /* unofficial code (0f) */
113 #define _df_d0_ fstp_i          /* unofficial code (17) */
114 #define _df_d8_ fstp_i          /* unofficial code (1f) */
115
116 static FUNC st_instr_table[64] = {
117         fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
118         fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
119         fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
120         fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
121         fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
122         fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
123         fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
124         fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
125 };
126 #else                           /* Support only documented FPU op-codes */
127
128 static FUNC st_instr_table[64] = {
129         fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
130         fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
131         fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
132         fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
133         fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
134         fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
135         fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
136         fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
137 };
138 #endif                          /* NO_UNDOC_CODE */
139
140
141 #define _NONE_ 0                /* Take no special action */
142 #define _REG0_ 1                /* Need to check for not empty st(0) */
143 #define _REGI_ 2                /* Need to check for not empty st(0) and
144                                  * st(rm) */
145 #define _REGi_ 0                /* Uses st(rm) */
146 #define _PUSH_ 3                /* Need to check for space to push onto stack */
147 #define _null_ 4                /* Function illegal or not implemented */
148 #define _REGIi 5                /* Uses st(0) and st(rm), result to st(rm) */
149 #define _REGIp 6                /* Uses st(0) and st(rm), result to st(rm)
150                                  * then pop */
151 #define _REGIc 0                /* Compare st(0) and st(rm) */
152 #define _REGIn 0                /* Uses st(0) and st(rm), but handle checks
153                                  * later */
154
155 #ifndef NO_UNDOC_CODE
156
157 /* Un-documented FPU op-codes supported by default. (see above) */
158
159 static unsigned char type_table[64] = {
160         _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
161         _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
162         _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
163         _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
164         _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
165         _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
166         _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
167         _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
168 };
169 #else                           /* Support only documented FPU op-codes */
170
171 static unsigned char type_table[64] = {
172         _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
173         _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
174         _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
175         _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
176         _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
177         _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
178         _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
179         _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
180 };
181 #endif                          /* NO_UNDOC_CODE */
182
183 /* Be careful when using any of these global variables...
184    they might change if swapping is triggered */
185 unsigned char FPU_rm;
186 char    FPU_st0_tag;
187 FPU_REG *FPU_st0_ptr;
188
189 #ifdef PARANOID
190 char    emulating = 0;
191 #endif                          /* PARANOID */
192
193 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
194 #define math_abort(signo) \
195     FPU_EIP = FPU_ORIG_EIP;REENTRANT_CHECK(OFF);return(signo);
196
197 static int
198 math_emulate(struct trapframe * tframe)
199 {
200
201         unsigned char FPU_modrm;
202         unsigned short code;
203 #ifdef LOOKAHEAD_LIMIT
204         int lookahead_limit = LOOKAHEAD_LIMIT;
205 #endif
206 #ifdef PARANOID
207         if (emulating) {
208                 printf("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
209         }
210         REENTRANT_CHECK(ON);
211 #endif                          /* PARANOID */
212
213         if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
214                 finit();
215                 control_word = __INITIAL_NPXCW__;
216                 ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP;
217         }
218         FPU_info = tframe;
219         FPU_ORIG_EIP = FPU_EIP; /* --pink-- */
220
221         if (FPU_CS != 0x001f) {
222                 printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP);
223                 panic("FPU emulation in kernel");
224         }
225 #ifdef notyet
226         /* We cannot handle emulation in v86-mode */
227         if (FPU_EFLAGS & 0x00020000) {
228                 FPU_ORIG_EIP = FPU_EIP;
229                 math_abort(FPU_info, SIGILL);
230         }
231 #endif
232
233         FPU_lookahead = FPU_LOOKAHEAD;
234         if (curproc->p_flag & P_TRACED)
235                 FPU_lookahead = 0;
236
237 do_another_FPU_instruction:
238
239         REENTRANT_CHECK(OFF);
240         code = fuword((u_int *) FPU_EIP);
241         REENTRANT_CHECK(ON);
242         if ((code & 0xff) == 0x9b) {    /* fwait */
243                 if (status_word & SW_Summary)
244                         goto do_the_FPU_interrupt;
245                 else {
246                         FPU_EIP++;
247                         goto FPU_instruction_done;
248                 }
249         }
250         if (status_word & SW_Summary) {
251                 /* Ignore the error for now if the current instruction is a
252                  * no-wait control instruction */
253                 /* The 80486 manual contradicts itself on this topic, so I use
254                  * the following list of such instructions until I can check
255                  * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv,
256                  * fnclex. */
257                 if (!((((code & 0xf803) == 0xe003) ||   /* fnclex, fninit,
258                                                          * fnstsw */
259                             (((code & 0x3003) == 0x3001) &&     /* fnsave, fnstcw,
260                                                                  * fnstenv, fnstsw */
261                                 ((code & 0xc000) != 0xc000))))) {
262                         /* This is a guess about what a real FPU might do to
263                          * this bit: */
264 /*        status_word &= ~SW_Summary; ****/
265
266                         /* We need to simulate the action of the kernel to FPU
267                          * interrupts here. Currently, the "real FPU" part of
268                          * the kernel (0.99.10) clears the exception flags,
269                          * sets the registers to empty, and passes information
270                          * back to the interrupted process via the cs selector
271                          * and operand selector, so we do the same. */
272         do_the_FPU_interrupt:
273                         cs_selector &= 0xffff0000;
274                         cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift);
275                         operand_selector = tag_word();
276                         status_word = 0;
277                         top = 0;
278                         {
279                                 int     r;
280                                 for (r = 0; r < 8; r++) {
281                                         regs[r].tag = TW_Empty;
282                                 }
283                         }
284                         REENTRANT_CHECK(OFF);
285                         math_abort(SIGFPE);
286                 }
287         }
288         FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
289
290         if ((code & 0xff) == 0x66) {    /* size prefix */
291                 FPU_EIP++;
292                 REENTRANT_CHECK(OFF);
293                 code = fuword((u_int *) FPU_EIP);
294                 REENTRANT_CHECK(ON);
295         }
296         FPU_EIP += 2;
297
298         FPU_modrm = code >> 8;
299         FPU_rm = FPU_modrm & 7;
300
301         if (FPU_modrm < 0300) {
302                 /* All of these instructions use the mod/rm byte to get a data
303                  * address */
304                 get_address(FPU_modrm);
305                 if (!(code & 1)) {
306                         unsigned short status1 = status_word;
307                         FPU_st0_ptr = &st(0);
308                         FPU_st0_tag = FPU_st0_ptr->tag;
309
310                         /* Stack underflow has priority */
311                         if (NOT_EMPTY_0) {
312                                 switch ((code >> 1) & 3) {
313                                 case 0:
314                                         reg_load_single();
315                                         break;
316                                 case 1:
317                                         reg_load_int32();
318                                         break;
319                                 case 2:
320                                         reg_load_double();
321                                         break;
322                                 case 3:
323                                         reg_load_int16();
324                                         break;
325                                 }
326
327                                 /* No more access to user memory, it is safe
328                                  * to use static data now */
329                                 FPU_st0_ptr = &st(0);
330                                 FPU_st0_tag = FPU_st0_ptr->tag;
331
332                                 /* NaN operands have the next priority. */
333                                 /* We have to delay looking at st(0) until
334                                  * after loading the data, because that data
335                                  * might contain an SNaN */
336                                 if ((FPU_st0_tag == TW_NaN) ||
337                                     (FPU_loaded_data.tag == TW_NaN)) {
338                                         /* Restore the status word; we might
339                                          * have loaded a denormal. */
340                                         status_word = status1;
341                                         if ((FPU_modrm & 0x30) == 0x10) {
342                                                 /* fcom or fcomp */
343                                                 EXCEPTION(EX_Invalid);
344                                                 setcc(SW_C3 | SW_C2 | SW_C0);
345                                                 if (FPU_modrm & 0x08)
346                                                         pop();  /* fcomp, so we pop. */
347                                         } else
348                                                 real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
349                                         goto reg_mem_instr_done;
350                                 }
351                                 switch ((FPU_modrm >> 3) & 7) {
352                                 case 0: /* fadd */
353                                         reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
354                                         break;
355                                 case 1: /* fmul */
356                                         reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
357                                         break;
358                                 case 2: /* fcom */
359                                         compare_st_data();
360                                         break;
361                                 case 3: /* fcomp */
362                                         compare_st_data();
363                                         pop();
364                                         break;
365                                 case 4: /* fsub */
366                                         reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
367                                         break;
368                                 case 5: /* fsubr */
369                                         reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
370                                         break;
371                                 case 6: /* fdiv */
372                                         reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
373                                         break;
374                                 case 7: /* fdivr */
375                                         if (FPU_st0_tag == TW_Zero)
376                                                 status_word = status1;  /* Undo any denorm tag,
377                                                                          * zero-divide has
378                                                                          * priority. */
379                                         reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
380                                         break;
381                                 }
382                         } else {
383                                 if ((FPU_modrm & 0x30) == 0x10) {
384                                         /* The instruction is fcom or fcomp */
385                                         EXCEPTION(EX_StackUnder);
386                                         setcc(SW_C3 | SW_C2 | SW_C0);
387                                         if (FPU_modrm & 0x08)
388                                                 pop();  /* fcomp, Empty or not,
389                                                          * we pop. */
390                                 } else
391                                         stack_underflow();
392                         }
393                 } else {
394                         load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
395                 }
396
397 reg_mem_instr_done:
398
399                 data_operand_offset = (intptr_t) (void *) FPU_data_address;
400         } else {
401                 /* None of these instructions access user memory */
402                 unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
403
404                 FPU_st0_ptr = &st(0);
405                 FPU_st0_tag = FPU_st0_ptr->tag;
406                 switch (type_table[(int) instr_index]) {
407                 case _NONE_:    /* also _REGIc: _REGIn */
408                         break;
409                 case _REG0_:
410                         if (!NOT_EMPTY_0) {
411                                 stack_underflow();
412                                 goto FPU_instruction_done;
413                         }
414                         break;
415                 case _REGIi:
416                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
417                                 stack_underflow_i(FPU_rm);
418                                 goto FPU_instruction_done;
419                         }
420                         break;
421                 case _REGIp:
422                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
423                                 stack_underflow_i(FPU_rm);
424                                 pop();
425                                 goto FPU_instruction_done;
426                         }
427                         break;
428                 case _REGI_:
429                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
430                                 stack_underflow();
431                                 goto FPU_instruction_done;
432                         }
433                         break;
434                 case _PUSH_:    /* Only used by the fld st(i) instruction */
435                         break;
436                 case _null_:
437                         Un_impl();
438                         goto FPU_instruction_done;
439                 default:
440                         EXCEPTION(EX_INTERNAL | 0x111);
441                         goto FPU_instruction_done;
442                 }
443                 (*st_instr_table[(int) instr_index]) ();
444         }
445
446 FPU_instruction_done:
447
448         ip_offset = FPU_entry_eip;
449         bswapw(code);
450         *(1 + (unsigned short *) &cs_selector) = code & 0x7ff;
451
452 #ifdef DEBUG
453         REENTRANT_CHECK(OFF);
454         emu_printall();
455         REENTRANT_CHECK(ON);
456 #endif                          /* DEBUG */
457 #ifdef LOOKAHEAD_LIMIT
458 if (--lookahead_limit)
459 #endif
460         if (FPU_lookahead) {
461                 unsigned char next;
462
463                 /* (This test should generate no machine code) */
464                 while (1) {
465                         REENTRANT_CHECK(OFF);
466                         next = fubyte((u_char *) FPU_EIP);
467                         REENTRANT_CHECK(ON);
468                         if (((next & 0xf8) == 0xd8) || (next == 0x9b)) {        /* fwait */
469                                 goto do_another_FPU_instruction;
470                         } else
471                                 if (next == 0x66) {     /* size prefix */
472                                         REENTRANT_CHECK(OFF);
473                                         next = fubyte((u_char *) (FPU_EIP + 1));
474                                         REENTRANT_CHECK(ON);
475                                         if ((next & 0xf8) == 0xd8) {
476                                                 FPU_EIP++;
477                                                 goto do_another_FPU_instruction;
478                                         }
479                                 }
480                         break;
481                 }
482         }
483         REENTRANT_CHECK(OFF);
484         return (0);             /* --pink-- */
485 }
486
487 static int
488 gnufpu_modevent(module_t mod, int type, void *unused) 
489 {
490         switch (type) {
491         case MOD_LOAD:
492                 if (pmath_emulate) {
493                         printf("Another Math emulator already present\n");
494                         return EACCES;
495                 }
496                 pmath_emulate = math_emulate;
497                 if (bootverbose)
498                         printf("GPL Math emulator present\n");
499                 break;
500         case MOD_UNLOAD:
501                 if (pmath_emulate != math_emulate) {
502                         printf("Cannot unload another math emulator\n");
503                         return EACCES;
504                 }
505                 pmath_emulate = 0;
506                 if (bootverbose)
507                         printf("GPL Math emulator unloaded\n");
508                 break;
509         default:
510                 break;
511         }
512         return 0;
513                 
514 }
515 static moduledata_t gnufpumod = {
516         "gnufpu",
517         gnufpu_modevent,
518         0
519 };
520 DECLARE_MODULE(gnufpu, gnufpumod, SI_SUB_DRIVERS, SI_ORDER_ANY);