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