|
|
1.1 root 1: /*
2: * linux/kernel/math/emulate.c
3: *
4: * (C) 1991 Linus Torvalds
5: */
6:
7: /*
8: * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
9: * even for soft-float, unless you use bruce evans' patches. The patches
10: * are great, but they have to be re-applied for every version, and the
11: * library is different for soft-float and 80387. So emulation is more
12: * practical, even though it's slower.
13: *
14: * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
15: * about add/sub/mul/div. Urgel. I should find some good source, but I'll
16: * just fake up something.
17: *
18: * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
19: * test every possible combination.
20: */
21:
22: /*
23: * This file is full of ugly macros etc: one problem was that gcc simply
24: * didn't want to make the structures as they should be: it has to try to
25: * align them. Sickening code, but at least I've hidden the ugly things
26: * in this one file: the other files don't need to know about these things.
27: *
28: * The other files also don't care about ST(x) etc - they just get addresses
29: * to 80-bit temporary reals, and do with them as they please. I wanted to
30: * hide most of the 387-specific things here.
31: */
32:
33: #ifdef KERNEL_MATH_EMULATION
34:
35: #include <signal.h>
36:
37: #define __ALIGNED_TEMP_REAL 1
38: #include <linux/math_emu.h>
39: #include <linux/kernel.h>
40: #include <asm/segment.h>
41:
42: #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
43: #define ST(x) (*__st((x)))
44: #define PST(x) ((const temp_real *) __st((x)))
45:
46: /*
47: * We don't want these inlined - it gets too messy in the machine-code.
48: */
49: static void fpop(void);
50: static void fpush(void);
51: static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
52: static temp_real_unaligned * __st(int i);
53:
54: static void do_emu(struct info * info)
55: {
56: unsigned short code;
57: temp_real tmp;
58: char * address;
59:
60: if (I387.cwd & I387.swd & 0x3f)
61: I387.swd |= 0x8000;
62: else
63: I387.swd &= 0x7fff;
64: ORIG_EIP = EIP;
65: /* 0x0007 means user code space */
66: if (CS != 0x000F) {
67: printk("math_emulate: %04x:%08x\n\r",CS,EIP);
68: panic("Math emulation needed in kernel");
69: }
70: code = get_fs_word((unsigned short *) EIP);
71: bswapw(code);
72: code &= 0x7ff;
73: I387.fip = EIP;
74: *(unsigned short *) &I387.fcs = CS;
75: *(1+(unsigned short *) &I387.fcs) = code;
76: EIP += 2;
77: switch (code) {
78: case 0x1d0: /* fnop */
79: return;
80: case 0x1d1: case 0x1d2: case 0x1d3:
81: case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
1.1.1.3 ! root 82: math_abort(info,SIGILL);
1.1 root 83: case 0x1e0:
84: ST(0).exponent ^= 0x8000;
85: return;
86: case 0x1e1:
87: ST(0).exponent &= 0x7fff;
88: return;
89: case 0x1e2: case 0x1e3:
1.1.1.3 ! root 90: math_abort(info,SIGILL);
1.1 root 91: case 0x1e4:
92: ftst(PST(0));
93: return;
94: case 0x1e5:
95: printk("fxam not implemented\n\r");
1.1.1.3 ! root 96: math_abort(info,SIGILL);
1.1 root 97: case 0x1e6: case 0x1e7:
1.1.1.3 ! root 98: math_abort(info,SIGILL);
1.1 root 99: case 0x1e8:
100: fpush();
101: ST(0) = CONST1;
102: return;
103: case 0x1e9:
104: fpush();
105: ST(0) = CONSTL2T;
106: return;
107: case 0x1ea:
108: fpush();
109: ST(0) = CONSTL2E;
110: return;
111: case 0x1eb:
112: fpush();
113: ST(0) = CONSTPI;
114: return;
115: case 0x1ec:
116: fpush();
117: ST(0) = CONSTLG2;
118: return;
119: case 0x1ed:
120: fpush();
121: ST(0) = CONSTLN2;
122: return;
123: case 0x1ee:
124: fpush();
125: ST(0) = CONSTZ;
126: return;
127: case 0x1ef:
1.1.1.3 ! root 128: math_abort(info,SIGILL);
1.1.1.2 root 129: case 0x1fa:
130: fsqrt(PST(0),&tmp);
131: real_to_real(&tmp,&ST(0));
132: return;
1.1 root 133: case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
134: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
1.1.1.2 root 135: case 0x1f8: case 0x1f9: case 0x1fb: case 0x1fd:
136: case 0x1fe: case 0x1ff:
1.1 root 137: printk("%04x fxxx not implemented\n\r",code + 0xd800);
1.1.1.3 ! root 138: math_abort(info,SIGILL);
1.1 root 139: case 0x1fc:
140: frndint(PST(0),&tmp);
141: real_to_real(&tmp,&ST(0));
142: return;
143: case 0x2e9:
144: fucom(PST(1),PST(0));
145: fpop(); fpop();
146: return;
147: case 0x3d0: case 0x3d1:
148: return;
149: case 0x3e2:
150: I387.swd &= 0x7f00;
151: return;
152: case 0x3e3:
153: I387.cwd = 0x037f;
154: I387.swd = 0x0000;
155: I387.twd = 0x0000;
156: return;
157: case 0x3e4:
158: return;
159: case 0x6d9:
160: fcom(PST(1),PST(0));
161: fpop(); fpop();
162: return;
163: case 0x7e0:
164: *(short *) &EAX = I387.swd;
165: return;
166: }
167: switch (code >> 3) {
168: case 0x18:
169: fadd(PST(0),PST(code & 7),&tmp);
170: real_to_real(&tmp,&ST(0));
171: return;
172: case 0x19:
173: fmul(PST(0),PST(code & 7),&tmp);
174: real_to_real(&tmp,&ST(0));
175: return;
176: case 0x1a:
177: fcom(PST(code & 7),PST(0));
178: return;
179: case 0x1b:
180: fcom(PST(code & 7),PST(0));
181: fpop();
182: return;
183: case 0x1c:
184: real_to_real(&ST(code & 7),&tmp);
185: tmp.exponent ^= 0x8000;
186: fadd(PST(0),&tmp,&tmp);
187: real_to_real(&tmp,&ST(0));
188: return;
189: case 0x1d:
190: ST(0).exponent ^= 0x8000;
191: fadd(PST(0),PST(code & 7),&tmp);
192: real_to_real(&tmp,&ST(0));
193: return;
194: case 0x1e:
195: fdiv(PST(0),PST(code & 7),&tmp);
196: real_to_real(&tmp,&ST(0));
197: return;
198: case 0x1f:
199: fdiv(PST(code & 7),PST(0),&tmp);
200: real_to_real(&tmp,&ST(0));
201: return;
202: case 0x38:
203: fpush();
204: ST(0) = ST((code+1) & 7);
205: return;
206: case 0x39:
207: fxchg(&ST(0),&ST(code & 7));
208: return;
209: case 0x3b:
210: ST(code & 7) = ST(0);
211: fpop();
212: return;
213: case 0x98:
214: fadd(PST(0),PST(code & 7),&tmp);
215: real_to_real(&tmp,&ST(code & 7));
216: return;
217: case 0x99:
218: fmul(PST(0),PST(code & 7),&tmp);
219: real_to_real(&tmp,&ST(code & 7));
220: return;
221: case 0x9a:
222: fcom(PST(code & 7),PST(0));
223: return;
224: case 0x9b:
225: fcom(PST(code & 7),PST(0));
226: fpop();
227: return;
228: case 0x9c:
229: ST(code & 7).exponent ^= 0x8000;
230: fadd(PST(0),PST(code & 7),&tmp);
231: real_to_real(&tmp,&ST(code & 7));
232: return;
233: case 0x9d:
234: real_to_real(&ST(0),&tmp);
235: tmp.exponent ^= 0x8000;
236: fadd(PST(code & 7),&tmp,&tmp);
237: real_to_real(&tmp,&ST(code & 7));
238: return;
239: case 0x9e:
240: fdiv(PST(0),PST(code & 7),&tmp);
241: real_to_real(&tmp,&ST(code & 7));
242: return;
243: case 0x9f:
244: fdiv(PST(code & 7),PST(0),&tmp);
245: real_to_real(&tmp,&ST(code & 7));
246: return;
247: case 0xb8:
248: printk("ffree not implemented\n\r");
1.1.1.3 ! root 249: math_abort(info,SIGILL);
1.1 root 250: case 0xb9:
251: fxchg(&ST(0),&ST(code & 7));
252: return;
253: case 0xba:
254: ST(code & 7) = ST(0);
255: return;
256: case 0xbb:
257: ST(code & 7) = ST(0);
258: fpop();
259: return;
260: case 0xbc:
261: fucom(PST(code & 7),PST(0));
262: return;
263: case 0xbd:
264: fucom(PST(code & 7),PST(0));
265: fpop();
266: return;
267: case 0xd8:
268: fadd(PST(code & 7),PST(0),&tmp);
269: real_to_real(&tmp,&ST(code & 7));
270: fpop();
271: return;
272: case 0xd9:
273: fmul(PST(code & 7),PST(0),&tmp);
274: real_to_real(&tmp,&ST(code & 7));
275: fpop();
276: return;
277: case 0xda:
278: fcom(PST(code & 7),PST(0));
279: fpop();
280: return;
281: case 0xdc:
282: ST(code & 7).exponent ^= 0x8000;
283: fadd(PST(0),PST(code & 7),&tmp);
284: real_to_real(&tmp,&ST(code & 7));
285: fpop();
286: return;
287: case 0xdd:
288: real_to_real(&ST(0),&tmp);
289: tmp.exponent ^= 0x8000;
290: fadd(PST(code & 7),&tmp,&tmp);
291: real_to_real(&tmp,&ST(code & 7));
292: fpop();
293: return;
294: case 0xde:
295: fdiv(PST(0),PST(code & 7),&tmp);
296: real_to_real(&tmp,&ST(code & 7));
297: fpop();
298: return;
299: case 0xdf:
300: fdiv(PST(code & 7),PST(0),&tmp);
301: real_to_real(&tmp,&ST(code & 7));
302: fpop();
303: return;
304: case 0xf8:
305: printk("ffree not implemented\n\r");
1.1.1.3 ! root 306: math_abort(info,SIGILL);
1.1 root 307: fpop();
308: return;
309: case 0xf9:
310: fxchg(&ST(0),&ST(code & 7));
311: return;
312: case 0xfa:
313: case 0xfb:
314: ST(code & 7) = ST(0);
315: fpop();
316: return;
317: }
318: switch ((code>>3) & 0xe7) {
319: case 0x22:
320: put_short_real(PST(0),info,code);
321: return;
322: case 0x23:
323: put_short_real(PST(0),info,code);
324: fpop();
325: return;
326: case 0x24:
327: address = ea(info,code);
328: for (code = 0 ; code < 7 ; code++) {
329: ((long *) & I387)[code] =
330: get_fs_long((unsigned long *) address);
331: address += 4;
332: }
333: return;
334: case 0x25:
335: address = ea(info,code);
336: *(unsigned short *) &I387.cwd =
337: get_fs_word((unsigned short *) address);
338: return;
339: case 0x26:
340: address = ea(info,code);
341: verify_area(address,28);
342: for (code = 0 ; code < 7 ; code++) {
343: put_fs_long( ((long *) & I387)[code],
344: (unsigned long *) address);
345: address += 4;
346: }
347: return;
348: case 0x27:
349: address = ea(info,code);
350: verify_area(address,2);
351: put_fs_word(I387.cwd,(short *) address);
352: return;
353: case 0x62:
354: put_long_int(PST(0),info,code);
355: return;
356: case 0x63:
357: put_long_int(PST(0),info,code);
358: fpop();
359: return;
360: case 0x65:
361: fpush();
362: get_temp_real(&tmp,info,code);
363: real_to_real(&tmp,&ST(0));
364: return;
365: case 0x67:
366: put_temp_real(PST(0),info,code);
367: fpop();
368: return;
369: case 0xa2:
370: put_long_real(PST(0),info,code);
371: return;
372: case 0xa3:
373: put_long_real(PST(0),info,code);
374: fpop();
375: return;
376: case 0xa4:
377: address = ea(info,code);
378: for (code = 0 ; code < 27 ; code++) {
379: ((long *) & I387)[code] =
380: get_fs_long((unsigned long *) address);
381: address += 4;
382: }
383: return;
384: case 0xa6:
385: address = ea(info,code);
386: verify_area(address,108);
387: for (code = 0 ; code < 27 ; code++) {
388: put_fs_long( ((long *) & I387)[code],
389: (unsigned long *) address);
390: address += 4;
391: }
392: I387.cwd = 0x037f;
393: I387.swd = 0x0000;
394: I387.twd = 0x0000;
395: return;
396: case 0xa7:
397: address = ea(info,code);
398: verify_area(address,2);
399: put_fs_word(I387.swd,(short *) address);
400: return;
401: case 0xe2:
402: put_short_int(PST(0),info,code);
403: return;
404: case 0xe3:
405: put_short_int(PST(0),info,code);
406: fpop();
407: return;
408: case 0xe4:
409: fpush();
410: get_BCD(&tmp,info,code);
411: real_to_real(&tmp,&ST(0));
412: return;
413: case 0xe5:
414: fpush();
415: get_longlong_int(&tmp,info,code);
416: real_to_real(&tmp,&ST(0));
417: return;
418: case 0xe6:
419: put_BCD(PST(0),info,code);
420: fpop();
421: return;
422: case 0xe7:
423: put_longlong_int(PST(0),info,code);
424: fpop();
425: return;
426: }
427: switch (code >> 9) {
428: case 0:
429: get_short_real(&tmp,info,code);
430: break;
431: case 1:
432: get_long_int(&tmp,info,code);
433: break;
434: case 2:
435: get_long_real(&tmp,info,code);
436: break;
437: case 4:
438: get_short_int(&tmp,info,code);
439: }
440: switch ((code>>3) & 0x27) {
441: case 0:
442: fadd(&tmp,PST(0),&tmp);
443: real_to_real(&tmp,&ST(0));
444: return;
445: case 1:
446: fmul(&tmp,PST(0),&tmp);
447: real_to_real(&tmp,&ST(0));
448: return;
449: case 2:
450: fcom(&tmp,PST(0));
451: return;
452: case 3:
453: fcom(&tmp,PST(0));
454: fpop();
455: return;
456: case 4:
457: tmp.exponent ^= 0x8000;
458: fadd(&tmp,PST(0),&tmp);
459: real_to_real(&tmp,&ST(0));
460: return;
461: case 5:
462: ST(0).exponent ^= 0x8000;
463: fadd(&tmp,PST(0),&tmp);
464: real_to_real(&tmp,&ST(0));
465: return;
466: case 6:
467: fdiv(PST(0),&tmp,&tmp);
468: real_to_real(&tmp,&ST(0));
469: return;
470: case 7:
471: fdiv(&tmp,PST(0),&tmp);
472: real_to_real(&tmp,&ST(0));
473: return;
474: }
475: if ((code & 0x138) == 0x100) {
476: fpush();
477: real_to_real(&tmp,&ST(0));
478: return;
479: }
480: printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
1.1.1.3 ! root 481: math_abort(info,SIGFPE);
1.1 root 482: }
483:
484: void math_emulate(long ___false)
485: {
486: if (!current->used_math) {
487: current->used_math = 1;
488: I387.cwd = 0x037f;
489: I387.swd = 0x0000;
490: I387.twd = 0x0000;
491: }
492: do_emu((struct info *) &___false);
493: }
494:
495: void __math_abort(struct info * info, unsigned int signal)
496: {
497: EIP = ORIG_EIP;
1.1.1.3 ! root 498: send_sig(signal,current,1);
1.1 root 499: __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
500: }
501:
502: static void fpop(void)
503: {
504: unsigned long tmp;
505:
506: tmp = I387.swd & 0xffffc7ff;
507: I387.swd += 0x00000800;
508: I387.swd &= 0x00003800;
509: I387.swd |= tmp;
510: }
511:
512: static void fpush(void)
513: {
514: unsigned long tmp;
515:
516: tmp = I387.swd & 0xffffc7ff;
517: I387.swd += 0x00003800;
518: I387.swd &= 0x00003800;
519: I387.swd |= tmp;
520: }
521:
522: static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
523: {
524: temp_real_unaligned c;
525:
526: c = *a;
527: *a = *b;
528: *b = c;
529: }
530:
531: static temp_real_unaligned * __st(int i)
532: {
533: i += I387.swd >> 11;
534: i &= 7;
535: return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
536: }
537:
538: #else /* no math emulation */
539:
540: #include <signal.h>
541: #include <linux/sched.h>
542:
543: void math_emulate(long ___false)
544: {
1.1.1.3 ! root 545: send_sig(SIGFPE,current,1);
1.1 root 546: schedule();
547: }
548:
549: #endif /* KERNEL_MATH_EMULATION */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.