Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/x86/math-emu/errors.c
10817 views
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 [email protected] |
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
37
void 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
*/
76
void FPU_illegal(void)
77
{
78
math_abort(FPU_info, SIGILL);
79
}
80
81
void 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
209
static 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
305
asmlinkage 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
#ifdef PRINT_MESSAGES
334
/* My message from the sponsor */
335
printk(FPU_VERSION " " __DATE__ " (C) W. Metzenthen.\n");
336
#endif /* PRINT_MESSAGES */
337
338
/* Get a name string for error reporting */
339
for (i = 0; exception_names[i].type; i++)
340
if ((exception_names[i].type & n) ==
341
exception_names[i].type)
342
break;
343
344
if (exception_names[i].type) {
345
#ifdef PRINT_MESSAGES
346
printk("FP Exception: %s!\n", exception_names[i].name);
347
#endif /* PRINT_MESSAGES */
348
} else
349
printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
350
351
if (n == EX_INTERNAL) {
352
printk("FPU emulator: Internal error type 0x%04x\n",
353
int_type);
354
FPU_printall();
355
}
356
#ifdef PRINT_MESSAGES
357
else
358
FPU_printall();
359
#endif /* PRINT_MESSAGES */
360
361
/*
362
* The 80486 generates an interrupt on the next non-control FPU
363
* instruction. So we need some means of flagging it.
364
* We use the ES (Error Summary) bit for this.
365
*/
366
}
367
RE_ENTRANT_CHECK_ON;
368
369
#ifdef __DEBUG__
370
math_abort(FPU_info, SIGFPE);
371
#endif /* __DEBUG__ */
372
373
}
374
375
/* Real operation attempted on a NaN. */
376
/* Returns < 0 if the exception is unmasked */
377
int real_1op_NaN(FPU_REG *a)
378
{
379
int signalling, isNaN;
380
381
isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000);
382
383
/* The default result for the case of two "equal" NaNs (signs may
384
differ) is chosen to reproduce 80486 behaviour */
385
signalling = isNaN && !(a->sigh & 0x40000000);
386
387
if (!signalling) {
388
if (!isNaN) { /* pseudo-NaN, or other unsupported? */
389
if (control_word & CW_Invalid) {
390
/* Masked response */
391
reg_copy(&CONST_QNaN, a);
392
}
393
EXCEPTION(EX_Invalid);
394
return (!(control_word & CW_Invalid) ? FPU_Exception :
395
0) | TAG_Special;
396
}
397
return TAG_Special;
398
}
399
400
if (control_word & CW_Invalid) {
401
/* The masked response */
402
if (!(a->sigh & 0x80000000)) { /* pseudo-NaN ? */
403
reg_copy(&CONST_QNaN, a);
404
}
405
/* ensure a Quiet NaN */
406
a->sigh |= 0x40000000;
407
}
408
409
EXCEPTION(EX_Invalid);
410
411
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
412
}
413
414
/* Real operation attempted on two operands, one a NaN. */
415
/* Returns < 0 if the exception is unmasked */
416
int real_2op_NaN(FPU_REG const *b, u_char tagb,
417
int deststnr, FPU_REG const *defaultNaN)
418
{
419
FPU_REG *dest = &st(deststnr);
420
FPU_REG const *a = dest;
421
u_char taga = FPU_gettagi(deststnr);
422
FPU_REG const *x;
423
int signalling, unsupported;
424
425
if (taga == TAG_Special)
426
taga = FPU_Special(a);
427
if (tagb == TAG_Special)
428
tagb = FPU_Special(b);
429
430
/* TW_NaN is also used for unsupported data types. */
431
unsupported = ((taga == TW_NaN)
432
&& !((exponent(a) == EXP_OVER)
433
&& (a->sigh & 0x80000000)))
434
|| ((tagb == TW_NaN)
435
&& !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)));
436
if (unsupported) {
437
if (control_word & CW_Invalid) {
438
/* Masked response */
439
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
440
}
441
EXCEPTION(EX_Invalid);
442
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) |
443
TAG_Special;
444
}
445
446
if (taga == TW_NaN) {
447
x = a;
448
if (tagb == TW_NaN) {
449
signalling = !(a->sigh & b->sigh & 0x40000000);
450
if (significand(b) > significand(a))
451
x = b;
452
else if (significand(b) == significand(a)) {
453
/* The default result for the case of two "equal" NaNs (signs may
454
differ) is chosen to reproduce 80486 behaviour */
455
x = defaultNaN;
456
}
457
} else {
458
/* return the quiet version of the NaN in a */
459
signalling = !(a->sigh & 0x40000000);
460
}
461
} else
462
#ifdef PARANOID
463
if (tagb == TW_NaN)
464
#endif /* PARANOID */
465
{
466
signalling = !(b->sigh & 0x40000000);
467
x = b;
468
}
469
#ifdef PARANOID
470
else {
471
signalling = 0;
472
EXCEPTION(EX_INTERNAL | 0x113);
473
x = &CONST_QNaN;
474
}
475
#endif /* PARANOID */
476
477
if ((!signalling) || (control_word & CW_Invalid)) {
478
if (!x)
479
x = b;
480
481
if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */
482
x = &CONST_QNaN;
483
484
FPU_copy_to_regi(x, TAG_Special, deststnr);
485
486
if (!signalling)
487
return TAG_Special;
488
489
/* ensure a Quiet NaN */
490
dest->sigh |= 0x40000000;
491
}
492
493
EXCEPTION(EX_Invalid);
494
495
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
496
}
497
498
/* Invalid arith operation on Valid registers */
499
/* Returns < 0 if the exception is unmasked */
500
asmlinkage int arith_invalid(int deststnr)
501
{
502
503
EXCEPTION(EX_Invalid);
504
505
if (control_word & CW_Invalid) {
506
/* The masked response */
507
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
508
}
509
510
return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid;
511
512
}
513
514
/* Divide a finite number by zero */
515
asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign)
516
{
517
FPU_REG *dest = &st(deststnr);
518
int tag = TAG_Valid;
519
520
if (control_word & CW_ZeroDiv) {
521
/* The masked response */
522
FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr);
523
setsign(dest, sign);
524
tag = TAG_Special;
525
}
526
527
EXCEPTION(EX_ZeroDiv);
528
529
return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag;
530
531
}
532
533
/* This may be called often, so keep it lean */
534
int set_precision_flag(int flags)
535
{
536
if (control_word & CW_Precision) {
537
partial_status &= ~(SW_C1 & flags);
538
partial_status |= flags; /* The masked response */
539
return 0;
540
} else {
541
EXCEPTION(flags);
542
return 1;
543
}
544
}
545
546
/* This may be called often, so keep it lean */
547
asmlinkage void set_precision_flag_up(void)
548
{
549
if (control_word & CW_Precision)
550
partial_status |= (SW_Precision | SW_C1); /* The masked response */
551
else
552
EXCEPTION(EX_Precision | SW_C1);
553
}
554
555
/* This may be called often, so keep it lean */
556
asmlinkage void set_precision_flag_down(void)
557
{
558
if (control_word & CW_Precision) { /* The masked response */
559
partial_status &= ~SW_C1;
560
partial_status |= SW_Precision;
561
} else
562
EXCEPTION(EX_Precision);
563
}
564
565
asmlinkage int denormal_operand(void)
566
{
567
if (control_word & CW_Denormal) { /* The masked response */
568
partial_status |= SW_Denorm_Op;
569
return TAG_Special;
570
} else {
571
EXCEPTION(EX_Denormal);
572
return TAG_Special | FPU_Exception;
573
}
574
}
575
576
asmlinkage int arith_overflow(FPU_REG *dest)
577
{
578
int tag = TAG_Valid;
579
580
if (control_word & CW_Overflow) {
581
/* The masked response */
582
/* ###### The response here depends upon the rounding mode */
583
reg_copy(&CONST_INF, dest);
584
tag = TAG_Special;
585
} else {
586
/* Subtract the magic number from the exponent */
587
addexponent(dest, (-3 * (1 << 13)));
588
}
589
590
EXCEPTION(EX_Overflow);
591
if (control_word & CW_Overflow) {
592
/* The overflow exception is masked. */
593
/* By definition, precision is lost.
594
The roundup bit (C1) is also set because we have
595
"rounded" upwards to Infinity. */
596
EXCEPTION(EX_Precision | SW_C1);
597
return tag;
598
}
599
600
return tag;
601
602
}
603
604
asmlinkage int arith_underflow(FPU_REG *dest)
605
{
606
int tag = TAG_Valid;
607
608
if (control_word & CW_Underflow) {
609
/* The masked response */
610
if (exponent16(dest) <= EXP_UNDER - 63) {
611
reg_copy(&CONST_Z, dest);
612
partial_status &= ~SW_C1; /* Round down. */
613
tag = TAG_Zero;
614
} else {
615
stdexp(dest);
616
}
617
} else {
618
/* Add the magic number to the exponent. */
619
addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias);
620
}
621
622
EXCEPTION(EX_Underflow);
623
if (control_word & CW_Underflow) {
624
/* The underflow exception is masked. */
625
EXCEPTION(EX_Precision);
626
return tag;
627
}
628
629
return tag;
630
631
}
632
633
void FPU_stack_overflow(void)
634
{
635
636
if (control_word & CW_Invalid) {
637
/* The masked response */
638
top--;
639
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
640
}
641
642
EXCEPTION(EX_StackOver);
643
644
return;
645
646
}
647
648
void FPU_stack_underflow(void)
649
{
650
651
if (control_word & CW_Invalid) {
652
/* The masked response */
653
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
654
}
655
656
EXCEPTION(EX_StackUnder);
657
658
return;
659
660
}
661
662
void FPU_stack_underflow_i(int i)
663
{
664
665
if (control_word & CW_Invalid) {
666
/* The masked response */
667
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
668
}
669
670
EXCEPTION(EX_StackUnder);
671
672
return;
673
674
}
675
676
void FPU_stack_underflow_pop(int i)
677
{
678
679
if (control_word & CW_Invalid) {
680
/* The masked response */
681
FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
682
FPU_pop();
683
}
684
685
EXCEPTION(EX_StackUnder);
686
687
return;
688
689
}
690
691