xref: /OK3568_Linux_fs/kernel/arch/x86/math-emu/errors.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*---------------------------------------------------------------------------+
3*4882a593Smuzhiyun  |  errors.c                                                                 |
4*4882a593Smuzhiyun  |                                                                           |
5*4882a593Smuzhiyun  |  The error handling functions for wm-FPU-emu                              |
6*4882a593Smuzhiyun  |                                                                           |
7*4882a593Smuzhiyun  | Copyright (C) 1992,1993,1994,1996                                         |
8*4882a593Smuzhiyun  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9*4882a593Smuzhiyun  |                  E-mail   billm@jacobi.maths.monash.edu.au                |
10*4882a593Smuzhiyun  |                                                                           |
11*4882a593Smuzhiyun  |                                                                           |
12*4882a593Smuzhiyun  +---------------------------------------------------------------------------*/
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun /*---------------------------------------------------------------------------+
15*4882a593Smuzhiyun  | Note:                                                                     |
16*4882a593Smuzhiyun  |    The file contains code which accesses user memory.                     |
17*4882a593Smuzhiyun  |    Emulator static data may change when user memory is accessed, due to   |
18*4882a593Smuzhiyun  |    other processes using the emulator while swapping is in progress.      |
19*4882a593Smuzhiyun  +---------------------------------------------------------------------------*/
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include <linux/signal.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include <linux/uaccess.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include "fpu_emu.h"
26*4882a593Smuzhiyun #include "fpu_system.h"
27*4882a593Smuzhiyun #include "exception.h"
28*4882a593Smuzhiyun #include "status_w.h"
29*4882a593Smuzhiyun #include "control_w.h"
30*4882a593Smuzhiyun #include "reg_constant.h"
31*4882a593Smuzhiyun #include "version.h"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /* */
34*4882a593Smuzhiyun #undef PRINT_MESSAGES
35*4882a593Smuzhiyun /* */
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #if 0
38*4882a593Smuzhiyun void Un_impl(void)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	u_char byte1, FPU_modrm;
41*4882a593Smuzhiyun 	unsigned long address = FPU_ORIG_EIP;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
44*4882a593Smuzhiyun 	/* No need to check access_ok(), we have previously fetched these bytes. */
45*4882a593Smuzhiyun 	printk("Unimplemented FPU Opcode at eip=%p : ", (void __user *)address);
46*4882a593Smuzhiyun 	if (FPU_CS == __USER_CS) {
47*4882a593Smuzhiyun 		while (1) {
48*4882a593Smuzhiyun 			FPU_get_user(byte1, (u_char __user *) address);
49*4882a593Smuzhiyun 			if ((byte1 & 0xf8) == 0xd8)
50*4882a593Smuzhiyun 				break;
51*4882a593Smuzhiyun 			printk("[%02x]", byte1);
52*4882a593Smuzhiyun 			address++;
53*4882a593Smuzhiyun 		}
54*4882a593Smuzhiyun 		printk("%02x ", byte1);
55*4882a593Smuzhiyun 		FPU_get_user(FPU_modrm, 1 + (u_char __user *) address);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 		if (FPU_modrm >= 0300)
58*4882a593Smuzhiyun 			printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8,
59*4882a593Smuzhiyun 			       FPU_modrm & 7);
60*4882a593Smuzhiyun 		else
61*4882a593Smuzhiyun 			printk("/%d\n", (FPU_modrm >> 3) & 7);
62*4882a593Smuzhiyun 	} else {
63*4882a593Smuzhiyun 		printk("cs selector = %04x\n", FPU_CS);
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	EXCEPTION(EX_Invalid);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun #endif /*  0  */
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun    Called for opcodes which are illegal and which are known to result in a
75*4882a593Smuzhiyun    SIGILL with a real 80486.
76*4882a593Smuzhiyun    */
FPU_illegal(void)77*4882a593Smuzhiyun void FPU_illegal(void)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	math_abort(FPU_info, SIGILL);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun 
FPU_printall(void)82*4882a593Smuzhiyun void FPU_printall(void)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	int i;
85*4882a593Smuzhiyun 	static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "Empty",
86*4882a593Smuzhiyun 		"DeNorm", "Inf", "NaN"
87*4882a593Smuzhiyun 	};
88*4882a593Smuzhiyun 	u_char byte1, FPU_modrm;
89*4882a593Smuzhiyun 	unsigned long address = FPU_ORIG_EIP;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
92*4882a593Smuzhiyun 	/* No need to check access_ok(), we have previously fetched these bytes. */
93*4882a593Smuzhiyun 	printk("At %p:", (void *)address);
94*4882a593Smuzhiyun 	if (FPU_CS == __USER_CS) {
95*4882a593Smuzhiyun #define MAX_PRINTED_BYTES 20
96*4882a593Smuzhiyun 		for (i = 0; i < MAX_PRINTED_BYTES; i++) {
97*4882a593Smuzhiyun 			FPU_get_user(byte1, (u_char __user *) address);
98*4882a593Smuzhiyun 			if ((byte1 & 0xf8) == 0xd8) {
99*4882a593Smuzhiyun 				printk(" %02x", byte1);
100*4882a593Smuzhiyun 				break;
101*4882a593Smuzhiyun 			}
102*4882a593Smuzhiyun 			printk(" [%02x]", byte1);
103*4882a593Smuzhiyun 			address++;
104*4882a593Smuzhiyun 		}
105*4882a593Smuzhiyun 		if (i == MAX_PRINTED_BYTES)
106*4882a593Smuzhiyun 			printk(" [more..]\n");
107*4882a593Smuzhiyun 		else {
108*4882a593Smuzhiyun 			FPU_get_user(FPU_modrm, 1 + (u_char __user *) address);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 			if (FPU_modrm >= 0300)
111*4882a593Smuzhiyun 				printk(" %02x (%02x+%d)\n", FPU_modrm,
112*4882a593Smuzhiyun 				       FPU_modrm & 0xf8, FPU_modrm & 7);
113*4882a593Smuzhiyun 			else
114*4882a593Smuzhiyun 				printk(" /%d, mod=%d rm=%d\n",
115*4882a593Smuzhiyun 				       (FPU_modrm >> 3) & 7,
116*4882a593Smuzhiyun 				       (FPU_modrm >> 6) & 3, FPU_modrm & 7);
117*4882a593Smuzhiyun 		}
118*4882a593Smuzhiyun 	} else {
119*4882a593Smuzhiyun 		printk("%04x\n", FPU_CS);
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	partial_status = status_word();
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun #ifdef DEBUGGING
125*4882a593Smuzhiyun 	if (partial_status & SW_Backward)
126*4882a593Smuzhiyun 		printk("SW: backward compatibility\n");
127*4882a593Smuzhiyun 	if (partial_status & SW_C3)
128*4882a593Smuzhiyun 		printk("SW: condition bit 3\n");
129*4882a593Smuzhiyun 	if (partial_status & SW_C2)
130*4882a593Smuzhiyun 		printk("SW: condition bit 2\n");
131*4882a593Smuzhiyun 	if (partial_status & SW_C1)
132*4882a593Smuzhiyun 		printk("SW: condition bit 1\n");
133*4882a593Smuzhiyun 	if (partial_status & SW_C0)
134*4882a593Smuzhiyun 		printk("SW: condition bit 0\n");
135*4882a593Smuzhiyun 	if (partial_status & SW_Summary)
136*4882a593Smuzhiyun 		printk("SW: exception summary\n");
137*4882a593Smuzhiyun 	if (partial_status & SW_Stack_Fault)
138*4882a593Smuzhiyun 		printk("SW: stack fault\n");
139*4882a593Smuzhiyun 	if (partial_status & SW_Precision)
140*4882a593Smuzhiyun 		printk("SW: loss of precision\n");
141*4882a593Smuzhiyun 	if (partial_status & SW_Underflow)
142*4882a593Smuzhiyun 		printk("SW: underflow\n");
143*4882a593Smuzhiyun 	if (partial_status & SW_Overflow)
144*4882a593Smuzhiyun 		printk("SW: overflow\n");
145*4882a593Smuzhiyun 	if (partial_status & SW_Zero_Div)
146*4882a593Smuzhiyun 		printk("SW: divide by zero\n");
147*4882a593Smuzhiyun 	if (partial_status & SW_Denorm_Op)
148*4882a593Smuzhiyun 		printk("SW: denormalized operand\n");
149*4882a593Smuzhiyun 	if (partial_status & SW_Invalid)
150*4882a593Smuzhiyun 		printk("SW: invalid operation\n");
151*4882a593Smuzhiyun #endif /* DEBUGGING */
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", partial_status & 0x8000 ? 1 : 0,	/* busy */
154*4882a593Smuzhiyun 	       (partial_status & 0x3800) >> 11,	/* stack top pointer */
155*4882a593Smuzhiyun 	       partial_status & 0x80 ? 1 : 0,	/* Error summary status */
156*4882a593Smuzhiyun 	       partial_status & 0x40 ? 1 : 0,	/* Stack flag */
157*4882a593Smuzhiyun 	       partial_status & SW_C3 ? 1 : 0, partial_status & SW_C2 ? 1 : 0,	/* cc */
158*4882a593Smuzhiyun 	       partial_status & SW_C1 ? 1 : 0, partial_status & SW_C0 ? 1 : 0,	/* cc */
159*4882a593Smuzhiyun 	       partial_status & SW_Precision ? 1 : 0,
160*4882a593Smuzhiyun 	       partial_status & SW_Underflow ? 1 : 0,
161*4882a593Smuzhiyun 	       partial_status & SW_Overflow ? 1 : 0,
162*4882a593Smuzhiyun 	       partial_status & SW_Zero_Div ? 1 : 0,
163*4882a593Smuzhiyun 	       partial_status & SW_Denorm_Op ? 1 : 0,
164*4882a593Smuzhiyun 	       partial_status & SW_Invalid ? 1 : 0);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d     ef=%d%d%d%d%d%d\n",
167*4882a593Smuzhiyun 	       control_word & 0x1000 ? 1 : 0,
168*4882a593Smuzhiyun 	       (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
169*4882a593Smuzhiyun 	       (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
170*4882a593Smuzhiyun 	       control_word & 0x80 ? 1 : 0,
171*4882a593Smuzhiyun 	       control_word & SW_Precision ? 1 : 0,
172*4882a593Smuzhiyun 	       control_word & SW_Underflow ? 1 : 0,
173*4882a593Smuzhiyun 	       control_word & SW_Overflow ? 1 : 0,
174*4882a593Smuzhiyun 	       control_word & SW_Zero_Div ? 1 : 0,
175*4882a593Smuzhiyun 	       control_word & SW_Denorm_Op ? 1 : 0,
176*4882a593Smuzhiyun 	       control_word & SW_Invalid ? 1 : 0);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	for (i = 0; i < 8; i++) {
179*4882a593Smuzhiyun 		FPU_REG *r = &st(i);
180*4882a593Smuzhiyun 		u_char tagi = FPU_gettagi(i);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 		switch (tagi) {
183*4882a593Smuzhiyun 		case TAG_Empty:
184*4882a593Smuzhiyun 			continue;
185*4882a593Smuzhiyun 		case TAG_Zero:
186*4882a593Smuzhiyun 		case TAG_Special:
187*4882a593Smuzhiyun 			/* Update tagi for the printk below */
188*4882a593Smuzhiyun 			tagi = FPU_Special(r);
189*4882a593Smuzhiyun 			fallthrough;
190*4882a593Smuzhiyun 		case TAG_Valid:
191*4882a593Smuzhiyun 			printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6d ", i,
192*4882a593Smuzhiyun 			       getsign(r) ? '-' : '+',
193*4882a593Smuzhiyun 			       (long)(r->sigh >> 16),
194*4882a593Smuzhiyun 			       (long)(r->sigh & 0xFFFF),
195*4882a593Smuzhiyun 			       (long)(r->sigl >> 16),
196*4882a593Smuzhiyun 			       (long)(r->sigl & 0xFFFF),
197*4882a593Smuzhiyun 			       exponent(r) - EXP_BIAS + 1);
198*4882a593Smuzhiyun 			break;
199*4882a593Smuzhiyun 		default:
200*4882a593Smuzhiyun 			printk("Whoops! Error in errors.c: tag%d is %d ", i,
201*4882a593Smuzhiyun 			       tagi);
202*4882a593Smuzhiyun 			continue;
203*4882a593Smuzhiyun 		}
204*4882a593Smuzhiyun 		printk("%s\n", tag_desc[(int)(unsigned)tagi]);
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun static struct {
212*4882a593Smuzhiyun 	int type;
213*4882a593Smuzhiyun 	const char *name;
214*4882a593Smuzhiyun } exception_names[] = {
215*4882a593Smuzhiyun 	{
216*4882a593Smuzhiyun 	EX_StackOver, "stack overflow"}, {
217*4882a593Smuzhiyun 	EX_StackUnder, "stack underflow"}, {
218*4882a593Smuzhiyun 	EX_Precision, "loss of precision"}, {
219*4882a593Smuzhiyun 	EX_Underflow, "underflow"}, {
220*4882a593Smuzhiyun 	EX_Overflow, "overflow"}, {
221*4882a593Smuzhiyun 	EX_ZeroDiv, "divide by zero"}, {
222*4882a593Smuzhiyun 	EX_Denormal, "denormalized operand"}, {
223*4882a593Smuzhiyun 	EX_Invalid, "invalid operation"}, {
224*4882a593Smuzhiyun 	EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION}, {
225*4882a593Smuzhiyun 	0, NULL}
226*4882a593Smuzhiyun };
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun /*
229*4882a593Smuzhiyun  EX_INTERNAL is always given with a code which indicates where the
230*4882a593Smuzhiyun  error was detected.
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun  Internal error types:
233*4882a593Smuzhiyun        0x14   in fpu_etc.c
234*4882a593Smuzhiyun        0x1nn  in a *.c file:
235*4882a593Smuzhiyun               0x101  in reg_add_sub.c
236*4882a593Smuzhiyun               0x102  in reg_mul.c
237*4882a593Smuzhiyun               0x104  in poly_atan.c
238*4882a593Smuzhiyun               0x105  in reg_mul.c
239*4882a593Smuzhiyun               0x107  in fpu_trig.c
240*4882a593Smuzhiyun 	      0x108  in reg_compare.c
241*4882a593Smuzhiyun 	      0x109  in reg_compare.c
242*4882a593Smuzhiyun 	      0x110  in reg_add_sub.c
243*4882a593Smuzhiyun 	      0x111  in fpe_entry.c
244*4882a593Smuzhiyun 	      0x112  in fpu_trig.c
245*4882a593Smuzhiyun 	      0x113  in errors.c
246*4882a593Smuzhiyun 	      0x115  in fpu_trig.c
247*4882a593Smuzhiyun 	      0x116  in fpu_trig.c
248*4882a593Smuzhiyun 	      0x117  in fpu_trig.c
249*4882a593Smuzhiyun 	      0x118  in fpu_trig.c
250*4882a593Smuzhiyun 	      0x119  in fpu_trig.c
251*4882a593Smuzhiyun 	      0x120  in poly_atan.c
252*4882a593Smuzhiyun 	      0x121  in reg_compare.c
253*4882a593Smuzhiyun 	      0x122  in reg_compare.c
254*4882a593Smuzhiyun 	      0x123  in reg_compare.c
255*4882a593Smuzhiyun 	      0x125  in fpu_trig.c
256*4882a593Smuzhiyun 	      0x126  in fpu_entry.c
257*4882a593Smuzhiyun 	      0x127  in poly_2xm1.c
258*4882a593Smuzhiyun 	      0x128  in fpu_entry.c
259*4882a593Smuzhiyun 	      0x129  in fpu_entry.c
260*4882a593Smuzhiyun 	      0x130  in get_address.c
261*4882a593Smuzhiyun 	      0x131  in get_address.c
262*4882a593Smuzhiyun 	      0x132  in get_address.c
263*4882a593Smuzhiyun 	      0x133  in get_address.c
264*4882a593Smuzhiyun 	      0x140  in load_store.c
265*4882a593Smuzhiyun 	      0x141  in load_store.c
266*4882a593Smuzhiyun               0x150  in poly_sin.c
267*4882a593Smuzhiyun               0x151  in poly_sin.c
268*4882a593Smuzhiyun 	      0x160  in reg_ld_str.c
269*4882a593Smuzhiyun 	      0x161  in reg_ld_str.c
270*4882a593Smuzhiyun 	      0x162  in reg_ld_str.c
271*4882a593Smuzhiyun 	      0x163  in reg_ld_str.c
272*4882a593Smuzhiyun 	      0x164  in reg_ld_str.c
273*4882a593Smuzhiyun 	      0x170  in fpu_tags.c
274*4882a593Smuzhiyun 	      0x171  in fpu_tags.c
275*4882a593Smuzhiyun 	      0x172  in fpu_tags.c
276*4882a593Smuzhiyun 	      0x180  in reg_convert.c
277*4882a593Smuzhiyun        0x2nn  in an *.S file:
278*4882a593Smuzhiyun               0x201  in reg_u_add.S
279*4882a593Smuzhiyun               0x202  in reg_u_div.S
280*4882a593Smuzhiyun               0x203  in reg_u_div.S
281*4882a593Smuzhiyun               0x204  in reg_u_div.S
282*4882a593Smuzhiyun               0x205  in reg_u_mul.S
283*4882a593Smuzhiyun               0x206  in reg_u_sub.S
284*4882a593Smuzhiyun               0x207  in wm_sqrt.S
285*4882a593Smuzhiyun 	      0x208  in reg_div.S
286*4882a593Smuzhiyun               0x209  in reg_u_sub.S
287*4882a593Smuzhiyun               0x210  in reg_u_sub.S
288*4882a593Smuzhiyun               0x211  in reg_u_sub.S
289*4882a593Smuzhiyun               0x212  in reg_u_sub.S
290*4882a593Smuzhiyun 	      0x213  in wm_sqrt.S
291*4882a593Smuzhiyun 	      0x214  in wm_sqrt.S
292*4882a593Smuzhiyun 	      0x215  in wm_sqrt.S
293*4882a593Smuzhiyun 	      0x220  in reg_norm.S
294*4882a593Smuzhiyun 	      0x221  in reg_norm.S
295*4882a593Smuzhiyun 	      0x230  in reg_round.S
296*4882a593Smuzhiyun 	      0x231  in reg_round.S
297*4882a593Smuzhiyun 	      0x232  in reg_round.S
298*4882a593Smuzhiyun 	      0x233  in reg_round.S
299*4882a593Smuzhiyun 	      0x234  in reg_round.S
300*4882a593Smuzhiyun 	      0x235  in reg_round.S
301*4882a593Smuzhiyun 	      0x236  in reg_round.S
302*4882a593Smuzhiyun 	      0x240  in div_Xsig.S
303*4882a593Smuzhiyun 	      0x241  in div_Xsig.S
304*4882a593Smuzhiyun 	      0x242  in div_Xsig.S
305*4882a593Smuzhiyun  */
306*4882a593Smuzhiyun 
FPU_exception(int n)307*4882a593Smuzhiyun asmlinkage __visible void FPU_exception(int n)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	int i, int_type;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	int_type = 0;		/* Needed only to stop compiler warnings */
312*4882a593Smuzhiyun 	if (n & EX_INTERNAL) {
313*4882a593Smuzhiyun 		int_type = n - EX_INTERNAL;
314*4882a593Smuzhiyun 		n = EX_INTERNAL;
315*4882a593Smuzhiyun 		/* Set lots of exception bits! */
316*4882a593Smuzhiyun 		partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
317*4882a593Smuzhiyun 	} else {
318*4882a593Smuzhiyun 		/* Extract only the bits which we use to set the status word */
319*4882a593Smuzhiyun 		n &= (SW_Exc_Mask);
320*4882a593Smuzhiyun 		/* Set the corresponding exception bit */
321*4882a593Smuzhiyun 		partial_status |= n;
322*4882a593Smuzhiyun 		/* Set summary bits iff exception isn't masked */
323*4882a593Smuzhiyun 		if (partial_status & ~control_word & CW_Exceptions)
324*4882a593Smuzhiyun 			partial_status |= (SW_Summary | SW_Backward);
325*4882a593Smuzhiyun 		if (n & (SW_Stack_Fault | EX_Precision)) {
326*4882a593Smuzhiyun 			if (!(n & SW_C1))
327*4882a593Smuzhiyun 				/* This bit distinguishes over- from underflow for a stack fault,
328*4882a593Smuzhiyun 				   and roundup from round-down for precision loss. */
329*4882a593Smuzhiyun 				partial_status &= ~SW_C1;
330*4882a593Smuzhiyun 		}
331*4882a593Smuzhiyun 	}
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
334*4882a593Smuzhiyun 	if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
335*4882a593Smuzhiyun 		/* Get a name string for error reporting */
336*4882a593Smuzhiyun 		for (i = 0; exception_names[i].type; i++)
337*4882a593Smuzhiyun 			if ((exception_names[i].type & n) ==
338*4882a593Smuzhiyun 			    exception_names[i].type)
339*4882a593Smuzhiyun 				break;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 		if (exception_names[i].type) {
342*4882a593Smuzhiyun #ifdef PRINT_MESSAGES
343*4882a593Smuzhiyun 			printk("FP Exception: %s!\n", exception_names[i].name);
344*4882a593Smuzhiyun #endif /* PRINT_MESSAGES */
345*4882a593Smuzhiyun 		} else
346*4882a593Smuzhiyun 			printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 		if (n == EX_INTERNAL) {
349*4882a593Smuzhiyun 			printk("FPU emulator: Internal error type 0x%04x\n",
350*4882a593Smuzhiyun 			       int_type);
351*4882a593Smuzhiyun 			FPU_printall();
352*4882a593Smuzhiyun 		}
353*4882a593Smuzhiyun #ifdef PRINT_MESSAGES
354*4882a593Smuzhiyun 		else
355*4882a593Smuzhiyun 			FPU_printall();
356*4882a593Smuzhiyun #endif /* PRINT_MESSAGES */
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 		/*
359*4882a593Smuzhiyun 		 * The 80486 generates an interrupt on the next non-control FPU
360*4882a593Smuzhiyun 		 * instruction. So we need some means of flagging it.
361*4882a593Smuzhiyun 		 * We use the ES (Error Summary) bit for this.
362*4882a593Smuzhiyun 		 */
363*4882a593Smuzhiyun 	}
364*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun #ifdef __DEBUG__
367*4882a593Smuzhiyun 	math_abort(FPU_info, SIGFPE);
368*4882a593Smuzhiyun #endif /* __DEBUG__ */
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun /* Real operation attempted on a NaN. */
373*4882a593Smuzhiyun /* Returns < 0 if the exception is unmasked */
real_1op_NaN(FPU_REG * a)374*4882a593Smuzhiyun int real_1op_NaN(FPU_REG *a)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	int signalling, isNaN;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	/* The default result for the case of two "equal" NaNs (signs may
381*4882a593Smuzhiyun 	   differ) is chosen to reproduce 80486 behaviour */
382*4882a593Smuzhiyun 	signalling = isNaN && !(a->sigh & 0x40000000);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (!signalling) {
385*4882a593Smuzhiyun 		if (!isNaN) {	/* pseudo-NaN, or other unsupported? */
386*4882a593Smuzhiyun 			if (control_word & CW_Invalid) {
387*4882a593Smuzhiyun 				/* Masked response */
388*4882a593Smuzhiyun 				reg_copy(&CONST_QNaN, a);
389*4882a593Smuzhiyun 			}
390*4882a593Smuzhiyun 			EXCEPTION(EX_Invalid);
391*4882a593Smuzhiyun 			return (!(control_word & CW_Invalid) ? FPU_Exception :
392*4882a593Smuzhiyun 				0) | TAG_Special;
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 		return TAG_Special;
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
398*4882a593Smuzhiyun 		/* The masked response */
399*4882a593Smuzhiyun 		if (!(a->sigh & 0x80000000)) {	/* pseudo-NaN ? */
400*4882a593Smuzhiyun 			reg_copy(&CONST_QNaN, a);
401*4882a593Smuzhiyun 		}
402*4882a593Smuzhiyun 		/* ensure a Quiet NaN */
403*4882a593Smuzhiyun 		a->sigh |= 0x40000000;
404*4882a593Smuzhiyun 	}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	EXCEPTION(EX_Invalid);
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun /* Real operation attempted on two operands, one a NaN. */
412*4882a593Smuzhiyun /* Returns < 0 if the exception is unmasked */
real_2op_NaN(FPU_REG const * b,u_char tagb,int deststnr,FPU_REG const * defaultNaN)413*4882a593Smuzhiyun int real_2op_NaN(FPU_REG const *b, u_char tagb,
414*4882a593Smuzhiyun 		 int deststnr, FPU_REG const *defaultNaN)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun 	FPU_REG *dest = &st(deststnr);
417*4882a593Smuzhiyun 	FPU_REG const *a = dest;
418*4882a593Smuzhiyun 	u_char taga = FPU_gettagi(deststnr);
419*4882a593Smuzhiyun 	FPU_REG const *x;
420*4882a593Smuzhiyun 	int signalling, unsupported;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	if (taga == TAG_Special)
423*4882a593Smuzhiyun 		taga = FPU_Special(a);
424*4882a593Smuzhiyun 	if (tagb == TAG_Special)
425*4882a593Smuzhiyun 		tagb = FPU_Special(b);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	/* TW_NaN is also used for unsupported data types. */
428*4882a593Smuzhiyun 	unsupported = ((taga == TW_NaN)
429*4882a593Smuzhiyun 		       && !((exponent(a) == EXP_OVER)
430*4882a593Smuzhiyun 			    && (a->sigh & 0x80000000)))
431*4882a593Smuzhiyun 	    || ((tagb == TW_NaN)
432*4882a593Smuzhiyun 		&& !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)));
433*4882a593Smuzhiyun 	if (unsupported) {
434*4882a593Smuzhiyun 		if (control_word & CW_Invalid) {
435*4882a593Smuzhiyun 			/* Masked response */
436*4882a593Smuzhiyun 			FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
437*4882a593Smuzhiyun 		}
438*4882a593Smuzhiyun 		EXCEPTION(EX_Invalid);
439*4882a593Smuzhiyun 		return (!(control_word & CW_Invalid) ? FPU_Exception : 0) |
440*4882a593Smuzhiyun 		    TAG_Special;
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	if (taga == TW_NaN) {
444*4882a593Smuzhiyun 		x = a;
445*4882a593Smuzhiyun 		if (tagb == TW_NaN) {
446*4882a593Smuzhiyun 			signalling = !(a->sigh & b->sigh & 0x40000000);
447*4882a593Smuzhiyun 			if (significand(b) > significand(a))
448*4882a593Smuzhiyun 				x = b;
449*4882a593Smuzhiyun 			else if (significand(b) == significand(a)) {
450*4882a593Smuzhiyun 				/* The default result for the case of two "equal" NaNs (signs may
451*4882a593Smuzhiyun 				   differ) is chosen to reproduce 80486 behaviour */
452*4882a593Smuzhiyun 				x = defaultNaN;
453*4882a593Smuzhiyun 			}
454*4882a593Smuzhiyun 		} else {
455*4882a593Smuzhiyun 			/* return the quiet version of the NaN in a */
456*4882a593Smuzhiyun 			signalling = !(a->sigh & 0x40000000);
457*4882a593Smuzhiyun 		}
458*4882a593Smuzhiyun 	} else
459*4882a593Smuzhiyun #ifdef PARANOID
460*4882a593Smuzhiyun 	if (tagb == TW_NaN)
461*4882a593Smuzhiyun #endif /* PARANOID */
462*4882a593Smuzhiyun 	{
463*4882a593Smuzhiyun 		signalling = !(b->sigh & 0x40000000);
464*4882a593Smuzhiyun 		x = b;
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun #ifdef PARANOID
467*4882a593Smuzhiyun 	else {
468*4882a593Smuzhiyun 		signalling = 0;
469*4882a593Smuzhiyun 		EXCEPTION(EX_INTERNAL | 0x113);
470*4882a593Smuzhiyun 		x = &CONST_QNaN;
471*4882a593Smuzhiyun 	}
472*4882a593Smuzhiyun #endif /* PARANOID */
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	if ((!signalling) || (control_word & CW_Invalid)) {
475*4882a593Smuzhiyun 		if (!x)
476*4882a593Smuzhiyun 			x = b;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 		if (!(x->sigh & 0x80000000))	/* pseudo-NaN ? */
479*4882a593Smuzhiyun 			x = &CONST_QNaN;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 		FPU_copy_to_regi(x, TAG_Special, deststnr);
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 		if (!signalling)
484*4882a593Smuzhiyun 			return TAG_Special;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 		/* ensure a Quiet NaN */
487*4882a593Smuzhiyun 		dest->sigh |= 0x40000000;
488*4882a593Smuzhiyun 	}
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	EXCEPTION(EX_Invalid);
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun /* Invalid arith operation on Valid registers */
496*4882a593Smuzhiyun /* Returns < 0 if the exception is unmasked */
arith_invalid(int deststnr)497*4882a593Smuzhiyun asmlinkage __visible int arith_invalid(int deststnr)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	EXCEPTION(EX_Invalid);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
503*4882a593Smuzhiyun 		/* The masked response */
504*4882a593Smuzhiyun 		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
505*4882a593Smuzhiyun 	}
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun /* Divide a finite number by zero */
FPU_divide_by_zero(int deststnr,u_char sign)512*4882a593Smuzhiyun asmlinkage __visible int FPU_divide_by_zero(int deststnr, u_char sign)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun 	FPU_REG *dest = &st(deststnr);
515*4882a593Smuzhiyun 	int tag = TAG_Valid;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	if (control_word & CW_ZeroDiv) {
518*4882a593Smuzhiyun 		/* The masked response */
519*4882a593Smuzhiyun 		FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr);
520*4882a593Smuzhiyun 		setsign(dest, sign);
521*4882a593Smuzhiyun 		tag = TAG_Special;
522*4882a593Smuzhiyun 	}
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	EXCEPTION(EX_ZeroDiv);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun /* This may be called often, so keep it lean */
set_precision_flag(int flags)531*4882a593Smuzhiyun int set_precision_flag(int flags)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun 	if (control_word & CW_Precision) {
534*4882a593Smuzhiyun 		partial_status &= ~(SW_C1 & flags);
535*4882a593Smuzhiyun 		partial_status |= flags;	/* The masked response */
536*4882a593Smuzhiyun 		return 0;
537*4882a593Smuzhiyun 	} else {
538*4882a593Smuzhiyun 		EXCEPTION(flags);
539*4882a593Smuzhiyun 		return 1;
540*4882a593Smuzhiyun 	}
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun /* This may be called often, so keep it lean */
set_precision_flag_up(void)544*4882a593Smuzhiyun asmlinkage __visible void set_precision_flag_up(void)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun 	if (control_word & CW_Precision)
547*4882a593Smuzhiyun 		partial_status |= (SW_Precision | SW_C1);	/* The masked response */
548*4882a593Smuzhiyun 	else
549*4882a593Smuzhiyun 		EXCEPTION(EX_Precision | SW_C1);
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun /* This may be called often, so keep it lean */
set_precision_flag_down(void)553*4882a593Smuzhiyun asmlinkage __visible void set_precision_flag_down(void)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun 	if (control_word & CW_Precision) {	/* The masked response */
556*4882a593Smuzhiyun 		partial_status &= ~SW_C1;
557*4882a593Smuzhiyun 		partial_status |= SW_Precision;
558*4882a593Smuzhiyun 	} else
559*4882a593Smuzhiyun 		EXCEPTION(EX_Precision);
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun 
denormal_operand(void)562*4882a593Smuzhiyun asmlinkage __visible int denormal_operand(void)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun 	if (control_word & CW_Denormal) {	/* The masked response */
565*4882a593Smuzhiyun 		partial_status |= SW_Denorm_Op;
566*4882a593Smuzhiyun 		return TAG_Special;
567*4882a593Smuzhiyun 	} else {
568*4882a593Smuzhiyun 		EXCEPTION(EX_Denormal);
569*4882a593Smuzhiyun 		return TAG_Special | FPU_Exception;
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
arith_overflow(FPU_REG * dest)573*4882a593Smuzhiyun asmlinkage __visible int arith_overflow(FPU_REG *dest)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun 	int tag = TAG_Valid;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	if (control_word & CW_Overflow) {
578*4882a593Smuzhiyun 		/* The masked response */
579*4882a593Smuzhiyun /* ###### The response here depends upon the rounding mode */
580*4882a593Smuzhiyun 		reg_copy(&CONST_INF, dest);
581*4882a593Smuzhiyun 		tag = TAG_Special;
582*4882a593Smuzhiyun 	} else {
583*4882a593Smuzhiyun 		/* Subtract the magic number from the exponent */
584*4882a593Smuzhiyun 		addexponent(dest, (-3 * (1 << 13)));
585*4882a593Smuzhiyun 	}
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	EXCEPTION(EX_Overflow);
588*4882a593Smuzhiyun 	if (control_word & CW_Overflow) {
589*4882a593Smuzhiyun 		/* The overflow exception is masked. */
590*4882a593Smuzhiyun 		/* By definition, precision is lost.
591*4882a593Smuzhiyun 		   The roundup bit (C1) is also set because we have
592*4882a593Smuzhiyun 		   "rounded" upwards to Infinity. */
593*4882a593Smuzhiyun 		EXCEPTION(EX_Precision | SW_C1);
594*4882a593Smuzhiyun 		return tag;
595*4882a593Smuzhiyun 	}
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	return tag;
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
arith_underflow(FPU_REG * dest)601*4882a593Smuzhiyun asmlinkage __visible int arith_underflow(FPU_REG *dest)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun 	int tag = TAG_Valid;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	if (control_word & CW_Underflow) {
606*4882a593Smuzhiyun 		/* The masked response */
607*4882a593Smuzhiyun 		if (exponent16(dest) <= EXP_UNDER - 63) {
608*4882a593Smuzhiyun 			reg_copy(&CONST_Z, dest);
609*4882a593Smuzhiyun 			partial_status &= ~SW_C1;	/* Round down. */
610*4882a593Smuzhiyun 			tag = TAG_Zero;
611*4882a593Smuzhiyun 		} else {
612*4882a593Smuzhiyun 			stdexp(dest);
613*4882a593Smuzhiyun 		}
614*4882a593Smuzhiyun 	} else {
615*4882a593Smuzhiyun 		/* Add the magic number to the exponent. */
616*4882a593Smuzhiyun 		addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias);
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	EXCEPTION(EX_Underflow);
620*4882a593Smuzhiyun 	if (control_word & CW_Underflow) {
621*4882a593Smuzhiyun 		/* The underflow exception is masked. */
622*4882a593Smuzhiyun 		EXCEPTION(EX_Precision);
623*4882a593Smuzhiyun 		return tag;
624*4882a593Smuzhiyun 	}
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	return tag;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
FPU_stack_overflow(void)630*4882a593Smuzhiyun void FPU_stack_overflow(void)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
634*4882a593Smuzhiyun 		/* The masked response */
635*4882a593Smuzhiyun 		top--;
636*4882a593Smuzhiyun 		FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
637*4882a593Smuzhiyun 	}
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	EXCEPTION(EX_StackOver);
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	return;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun 
FPU_stack_underflow(void)645*4882a593Smuzhiyun void FPU_stack_underflow(void)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
649*4882a593Smuzhiyun 		/* The masked response */
650*4882a593Smuzhiyun 		FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
651*4882a593Smuzhiyun 	}
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	EXCEPTION(EX_StackUnder);
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	return;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun 
FPU_stack_underflow_i(int i)659*4882a593Smuzhiyun void FPU_stack_underflow_i(int i)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
663*4882a593Smuzhiyun 		/* The masked response */
664*4882a593Smuzhiyun 		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	EXCEPTION(EX_StackUnder);
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	return;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun 
FPU_stack_underflow_pop(int i)673*4882a593Smuzhiyun void FPU_stack_underflow_pop(int i)
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	if (control_word & CW_Invalid) {
677*4882a593Smuzhiyun 		/* The masked response */
678*4882a593Smuzhiyun 		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
679*4882a593Smuzhiyun 		FPU_pop();
680*4882a593Smuzhiyun 	}
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	EXCEPTION(EX_StackUnder);
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	return;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun }
687