Merge from vendor branch DIFFUTILS:
[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.4 2003/08/07 21:17:20 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 "fpu_emu.h"
84 #include "fpu_system.h"
85 #include "exception.h"
86 #include "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         KKASSERT(curproc);
214         if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
215                 finit();
216                 control_word = __INITIAL_NPXCW__;
217                 ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP;
218         }
219         FPU_info = tframe;
220         FPU_ORIG_EIP = FPU_EIP; /* --pink-- */
221
222         if (FPU_CS != 0x001f) {
223                 printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP);
224                 panic("FPU emulation in kernel");
225         }
226 #ifdef notyet
227         /* We cannot handle emulation in v86-mode */
228         if (FPU_EFLAGS & 0x00020000) {
229                 FPU_ORIG_EIP = FPU_EIP;
230                 math_abort(FPU_info, SIGILL);
231         }
232 #endif
233
234         FPU_lookahead = FPU_LOOKAHEAD;
235         if (curproc->p_flag & P_TRACED)
236                 FPU_lookahead = 0;
237
238 do_another_FPU_instruction:
239
240         REENTRANT_CHECK(OFF);
241         code = fuword((u_int *) FPU_EIP);
242         REENTRANT_CHECK(ON);
243         if ((code & 0xff) == 0x9b) {    /* fwait */
244                 if (status_word & SW_Summary)
245                         goto do_the_FPU_interrupt;
246                 else {
247                         FPU_EIP++;
248                         goto FPU_instruction_done;
249                 }
250         }
251         if (status_word & SW_Summary) {
252                 /* Ignore the error for now if the current instruction is a
253                  * no-wait control instruction */
254                 /* The 80486 manual contradicts itself on this topic, so I use
255                  * the following list of such instructions until I can check
256                  * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv,
257                  * fnclex. */
258                 if (!((((code & 0xf803) == 0xe003) ||   /* fnclex, fninit,
259                                                          * fnstsw */
260                             (((code & 0x3003) == 0x3001) &&     /* fnsave, fnstcw,
261                                                                  * fnstenv, fnstsw */
262                                 ((code & 0xc000) != 0xc000))))) {
263                         /* This is a guess about what a real FPU might do to
264                          * this bit: */
265 /*        status_word &= ~SW_Summary; ****/
266
267                         /* We need to simulate the action of the kernel to FPU
268                          * interrupts here. Currently, the "real FPU" part of
269                          * the kernel (0.99.10) clears the exception flags,
270                          * sets the registers to empty, and passes information
271                          * back to the interrupted process via the cs selector
272                          * and operand selector, so we do the same. */
273         do_the_FPU_interrupt:
274                         cs_selector &= 0xffff0000;
275                         cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift);
276                         operand_selector = tag_word();
277                         status_word = 0;
278                         top = 0;
279                         {
280                                 int     r;
281                                 for (r = 0; r < 8; r++) {
282                                         regs[r].tag = TW_Empty;
283                                 }
284                         }
285                         REENTRANT_CHECK(OFF);
286                         math_abort(SIGFPE);
287                 }
288         }
289         FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
290
291         if ((code & 0xff) == 0x66) {    /* size prefix */
292                 FPU_EIP++;
293                 REENTRANT_CHECK(OFF);
294                 code = fuword((u_int *) FPU_EIP);
295                 REENTRANT_CHECK(ON);
296         }
297         FPU_EIP += 2;
298
299         FPU_modrm = code >> 8;
300         FPU_rm = FPU_modrm & 7;
301
302         if (FPU_modrm < 0300) {
303                 /* All of these instructions use the mod/rm byte to get a data
304                  * address */
305                 get_address(FPU_modrm);
306                 if (!(code & 1)) {
307                         unsigned short status1 = status_word;
308                         FPU_st0_ptr = &st(0);
309                         FPU_st0_tag = FPU_st0_ptr->tag;
310
311                         /* Stack underflow has priority */
312                         if (NOT_EMPTY_0) {
313                                 switch ((code >> 1) & 3) {
314                                 case 0:
315                                         reg_load_single();
316                                         break;
317                                 case 1:
318                                         reg_load_int32();
319                                         break;
320                                 case 2:
321                                         reg_load_double();
322                                         break;
323                                 case 3:
324                                         reg_load_int16();
325                                         break;
326                                 }
327
328                                 /* No more access to user memory, it is safe
329                                  * to use static data now */
330                                 FPU_st0_ptr = &st(0);
331                                 FPU_st0_tag = FPU_st0_ptr->tag;
332
333                                 /* NaN operands have the next priority. */
334                                 /* We have to delay looking at st(0) until
335                                  * after loading the data, because that data
336                                  * might contain an SNaN */
337                                 if ((FPU_st0_tag == TW_NaN) ||
338                                     (FPU_loaded_data.tag == TW_NaN)) {
339                                         /* Restore the status word; we might
340                                          * have loaded a denormal. */
341                                         status_word = status1;
342                                         if ((FPU_modrm & 0x30) == 0x10) {
343                                                 /* fcom or fcomp */
344                                                 EXCEPTION(EX_Invalid);
345                                                 setcc(SW_C3 | SW_C2 | SW_C0);
346                                                 if (FPU_modrm & 0x08)
347                                                         pop();  /* fcomp, so we pop. */
348                                         } else
349                                                 real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
350                                         goto reg_mem_instr_done;
351                                 }
352                                 switch ((FPU_modrm >> 3) & 7) {
353                                 case 0: /* fadd */
354                                         reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
355                                         break;
356                                 case 1: /* fmul */
357                                         reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
358                                         break;
359                                 case 2: /* fcom */
360                                         compare_st_data();
361                                         break;
362                                 case 3: /* fcomp */
363                                         compare_st_data();
364                                         pop();
365                                         break;
366                                 case 4: /* fsub */
367                                         reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
368                                         break;
369                                 case 5: /* fsubr */
370                                         reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
371                                         break;
372                                 case 6: /* fdiv */
373                                         reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word);
374                                         break;
375                                 case 7: /* fdivr */
376                                         if (FPU_st0_tag == TW_Zero)
377                                                 status_word = status1;  /* Undo any denorm tag,
378                                                                          * zero-divide has
379                                                                          * priority. */
380                                         reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word);
381                                         break;
382                                 }
383                         } else {
384                                 if ((FPU_modrm & 0x30) == 0x10) {
385                                         /* The instruction is fcom or fcomp */
386                                         EXCEPTION(EX_StackUnder);
387                                         setcc(SW_C3 | SW_C2 | SW_C0);
388                                         if (FPU_modrm & 0x08)
389                                                 pop();  /* fcomp, Empty or not,
390                                                          * we pop. */
391                                 } else
392                                         stack_underflow();
393                         }
394                 } else {
395                         load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
396                 }
397
398 reg_mem_instr_done:
399
400                 data_operand_offset = (intptr_t) (void *) FPU_data_address;
401         } else {
402                 /* None of these instructions access user memory */
403                 unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
404
405                 FPU_st0_ptr = &st(0);
406                 FPU_st0_tag = FPU_st0_ptr->tag;
407                 switch (type_table[(int) instr_index]) {
408                 case _NONE_:    /* also _REGIc: _REGIn */
409                         break;
410                 case _REG0_:
411                         if (!NOT_EMPTY_0) {
412                                 stack_underflow();
413                                 goto FPU_instruction_done;
414                         }
415                         break;
416                 case _REGIi:
417                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
418                                 stack_underflow_i(FPU_rm);
419                                 goto FPU_instruction_done;
420                         }
421                         break;
422                 case _REGIp:
423                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
424                                 stack_underflow_i(FPU_rm);
425                                 pop();
426                                 goto FPU_instruction_done;
427                         }
428                         break;
429                 case _REGI_:
430                         if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) {
431                                 stack_underflow();
432                                 goto FPU_instruction_done;
433                         }
434                         break;
435                 case _PUSH_:    /* Only used by the fld st(i) instruction */
436                         break;
437                 case _null_:
438                         Un_impl();
439                         goto FPU_instruction_done;
440                 default:
441                         EXCEPTION(EX_INTERNAL | 0x111);
442                         goto FPU_instruction_done;
443                 }
444                 (*st_instr_table[(int) instr_index]) ();
445         }
446
447 FPU_instruction_done:
448
449         ip_offset = FPU_entry_eip;
450         bswapw(code);
451         *(1 + (unsigned short *) &cs_selector) = code & 0x7ff;
452
453 #ifdef DEBUG
454         REENTRANT_CHECK(OFF);
455         emu_printall();
456         REENTRANT_CHECK(ON);
457 #endif                          /* DEBUG */
458 #ifdef LOOKAHEAD_LIMIT
459 if (--lookahead_limit)
460 #endif
461         if (FPU_lookahead) {
462                 unsigned char next;
463
464                 /* (This test should generate no machine code) */
465                 while (1) {
466                         REENTRANT_CHECK(OFF);
467                         next = fubyte((u_char *) FPU_EIP);
468                         REENTRANT_CHECK(ON);
469                         if (((next & 0xf8) == 0xd8) || (next == 0x9b)) {        /* fwait */
470                                 goto do_another_FPU_instruction;
471                         } else
472                                 if (next == 0x66) {     /* size prefix */
473                                         REENTRANT_CHECK(OFF);
474                                         next = fubyte((u_char *) (FPU_EIP + 1));
475                                         REENTRANT_CHECK(ON);
476                                         if ((next & 0xf8) == 0xd8) {
477                                                 FPU_EIP++;
478                                                 goto do_another_FPU_instruction;
479                                         }
480                                 }
481                         break;
482                 }
483         }
484         REENTRANT_CHECK(OFF);
485         return (0);             /* --pink-- */
486 }
487
488 static int
489 gnufpu_modevent(module_t mod, int type, void *unused) 
490 {
491         switch (type) {
492         case MOD_LOAD:
493                 if (pmath_emulate) {
494                         printf("Another Math emulator already present\n");
495                         return EACCES;
496                 }
497                 pmath_emulate = math_emulate;
498                 if (bootverbose)
499                         printf("GPL Math emulator present\n");
500                 break;
501         case MOD_UNLOAD:
502                 if (pmath_emulate != math_emulate) {
503                         printf("Cannot unload another math emulator\n");
504                         return EACCES;
505                 }
506                 pmath_emulate = 0;
507                 if (bootverbose)
508                         printf("GPL Math emulator unloaded\n");
509                 break;
510         default:
511                 break;
512         }
513         return 0;
514                 
515 }
516 static moduledata_t gnufpumod = {
517         "gnufpu",
518         gnufpu_modevent,
519         0
520 };
521 DECLARE_MODULE(gnufpu, gnufpumod, SI_SUB_DRIVERS, SI_ORDER_ANY);