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