|
|
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:
82: math_abort(info,1<<(SIGILL-1));
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:
90: math_abort(info,1<<(SIGILL-1));
91: case 0x1e4:
92: ftst(PST(0));
93: return;
94: case 0x1e5:
95: printk("fxam not implemented\n\r");
96: math_abort(info,1<<(SIGILL-1));
97: case 0x1e6: case 0x1e7:
98: math_abort(info,1<<(SIGILL-1));
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:
128: math_abort(info,1<<(SIGILL-1));
129: case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
130: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
131: case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb:
132: case 0x1fd: case 0x1fe: case 0x1ff:
133: printk("%04x fxxx not implemented\n\r",code + 0xd800);
134: math_abort(info,1<<(SIGILL-1));
135: case 0x1fc:
136: frndint(PST(0),&tmp);
137: real_to_real(&tmp,&ST(0));
138: return;
139: case 0x2e9:
140: fucom(PST(1),PST(0));
141: fpop(); fpop();
142: return;
143: case 0x3d0: case 0x3d1:
144: return;
145: case 0x3e2:
146: I387.swd &= 0x7f00;
147: return;
148: case 0x3e3:
149: I387.cwd = 0x037f;
150: I387.swd = 0x0000;
151: I387.twd = 0x0000;
152: return;
153: case 0x3e4:
154: return;
155: case 0x6d9:
156: fcom(PST(1),PST(0));
157: fpop(); fpop();
158: return;
159: case 0x7e0:
160: *(short *) &EAX = I387.swd;
161: return;
162: }
163: switch (code >> 3) {
164: case 0x18:
165: fadd(PST(0),PST(code & 7),&tmp);
166: real_to_real(&tmp,&ST(0));
167: return;
168: case 0x19:
169: fmul(PST(0),PST(code & 7),&tmp);
170: real_to_real(&tmp,&ST(0));
171: return;
172: case 0x1a:
173: fcom(PST(code & 7),PST(0));
174: return;
175: case 0x1b:
176: fcom(PST(code & 7),PST(0));
177: fpop();
178: return;
179: case 0x1c:
180: real_to_real(&ST(code & 7),&tmp);
181: tmp.exponent ^= 0x8000;
182: fadd(PST(0),&tmp,&tmp);
183: real_to_real(&tmp,&ST(0));
184: return;
185: case 0x1d:
186: ST(0).exponent ^= 0x8000;
187: fadd(PST(0),PST(code & 7),&tmp);
188: real_to_real(&tmp,&ST(0));
189: return;
190: case 0x1e:
191: fdiv(PST(0),PST(code & 7),&tmp);
192: real_to_real(&tmp,&ST(0));
193: return;
194: case 0x1f:
195: fdiv(PST(code & 7),PST(0),&tmp);
196: real_to_real(&tmp,&ST(0));
197: return;
198: case 0x38:
199: fpush();
200: ST(0) = ST((code+1) & 7);
201: return;
202: case 0x39:
203: fxchg(&ST(0),&ST(code & 7));
204: return;
205: case 0x3b:
206: ST(code & 7) = ST(0);
207: fpop();
208: return;
209: case 0x98:
210: fadd(PST(0),PST(code & 7),&tmp);
211: real_to_real(&tmp,&ST(code & 7));
212: return;
213: case 0x99:
214: fmul(PST(0),PST(code & 7),&tmp);
215: real_to_real(&tmp,&ST(code & 7));
216: return;
217: case 0x9a:
218: fcom(PST(code & 7),PST(0));
219: return;
220: case 0x9b:
221: fcom(PST(code & 7),PST(0));
222: fpop();
223: return;
224: case 0x9c:
225: ST(code & 7).exponent ^= 0x8000;
226: fadd(PST(0),PST(code & 7),&tmp);
227: real_to_real(&tmp,&ST(code & 7));
228: return;
229: case 0x9d:
230: real_to_real(&ST(0),&tmp);
231: tmp.exponent ^= 0x8000;
232: fadd(PST(code & 7),&tmp,&tmp);
233: real_to_real(&tmp,&ST(code & 7));
234: return;
235: case 0x9e:
236: fdiv(PST(0),PST(code & 7),&tmp);
237: real_to_real(&tmp,&ST(code & 7));
238: return;
239: case 0x9f:
240: fdiv(PST(code & 7),PST(0),&tmp);
241: real_to_real(&tmp,&ST(code & 7));
242: return;
243: case 0xb8:
244: printk("ffree not implemented\n\r");
245: math_abort(info,1<<(SIGILL-1));
246: case 0xb9:
247: fxchg(&ST(0),&ST(code & 7));
248: return;
249: case 0xba:
250: ST(code & 7) = ST(0);
251: return;
252: case 0xbb:
253: ST(code & 7) = ST(0);
254: fpop();
255: return;
256: case 0xbc:
257: fucom(PST(code & 7),PST(0));
258: return;
259: case 0xbd:
260: fucom(PST(code & 7),PST(0));
261: fpop();
262: return;
263: case 0xd8:
264: fadd(PST(code & 7),PST(0),&tmp);
265: real_to_real(&tmp,&ST(code & 7));
266: fpop();
267: return;
268: case 0xd9:
269: fmul(PST(code & 7),PST(0),&tmp);
270: real_to_real(&tmp,&ST(code & 7));
271: fpop();
272: return;
273: case 0xda:
274: fcom(PST(code & 7),PST(0));
275: fpop();
276: return;
277: case 0xdc:
278: ST(code & 7).exponent ^= 0x8000;
279: fadd(PST(0),PST(code & 7),&tmp);
280: real_to_real(&tmp,&ST(code & 7));
281: fpop();
282: return;
283: case 0xdd:
284: real_to_real(&ST(0),&tmp);
285: tmp.exponent ^= 0x8000;
286: fadd(PST(code & 7),&tmp,&tmp);
287: real_to_real(&tmp,&ST(code & 7));
288: fpop();
289: return;
290: case 0xde:
291: fdiv(PST(0),PST(code & 7),&tmp);
292: real_to_real(&tmp,&ST(code & 7));
293: fpop();
294: return;
295: case 0xdf:
296: fdiv(PST(code & 7),PST(0),&tmp);
297: real_to_real(&tmp,&ST(code & 7));
298: fpop();
299: return;
300: case 0xf8:
301: printk("ffree not implemented\n\r");
302: math_abort(info,1<<(SIGILL-1));
303: fpop();
304: return;
305: case 0xf9:
306: fxchg(&ST(0),&ST(code & 7));
307: return;
308: case 0xfa:
309: case 0xfb:
310: ST(code & 7) = ST(0);
311: fpop();
312: return;
313: }
314: switch ((code>>3) & 0xe7) {
315: case 0x22:
316: put_short_real(PST(0),info,code);
317: return;
318: case 0x23:
319: put_short_real(PST(0),info,code);
320: fpop();
321: return;
322: case 0x24:
323: address = ea(info,code);
324: for (code = 0 ; code < 7 ; code++) {
325: ((long *) & I387)[code] =
326: get_fs_long((unsigned long *) address);
327: address += 4;
328: }
329: return;
330: case 0x25:
331: address = ea(info,code);
332: *(unsigned short *) &I387.cwd =
333: get_fs_word((unsigned short *) address);
334: return;
335: case 0x26:
336: address = ea(info,code);
337: verify_area(address,28);
338: for (code = 0 ; code < 7 ; code++) {
339: put_fs_long( ((long *) & I387)[code],
340: (unsigned long *) address);
341: address += 4;
342: }
343: return;
344: case 0x27:
345: address = ea(info,code);
346: verify_area(address,2);
347: put_fs_word(I387.cwd,(short *) address);
348: return;
349: case 0x62:
350: put_long_int(PST(0),info,code);
351: return;
352: case 0x63:
353: put_long_int(PST(0),info,code);
354: fpop();
355: return;
356: case 0x65:
357: fpush();
358: get_temp_real(&tmp,info,code);
359: real_to_real(&tmp,&ST(0));
360: return;
361: case 0x67:
362: put_temp_real(PST(0),info,code);
363: fpop();
364: return;
365: case 0xa2:
366: put_long_real(PST(0),info,code);
367: return;
368: case 0xa3:
369: put_long_real(PST(0),info,code);
370: fpop();
371: return;
372: case 0xa4:
373: address = ea(info,code);
374: for (code = 0 ; code < 27 ; code++) {
375: ((long *) & I387)[code] =
376: get_fs_long((unsigned long *) address);
377: address += 4;
378: }
379: return;
380: case 0xa6:
381: address = ea(info,code);
382: verify_area(address,108);
383: for (code = 0 ; code < 27 ; code++) {
384: put_fs_long( ((long *) & I387)[code],
385: (unsigned long *) address);
386: address += 4;
387: }
388: I387.cwd = 0x037f;
389: I387.swd = 0x0000;
390: I387.twd = 0x0000;
391: return;
392: case 0xa7:
393: address = ea(info,code);
394: verify_area(address,2);
395: put_fs_word(I387.swd,(short *) address);
396: return;
397: case 0xe2:
398: put_short_int(PST(0),info,code);
399: return;
400: case 0xe3:
401: put_short_int(PST(0),info,code);
402: fpop();
403: return;
404: case 0xe4:
405: fpush();
406: get_BCD(&tmp,info,code);
407: real_to_real(&tmp,&ST(0));
408: return;
409: case 0xe5:
410: fpush();
411: get_longlong_int(&tmp,info,code);
412: real_to_real(&tmp,&ST(0));
413: return;
414: case 0xe6:
415: put_BCD(PST(0),info,code);
416: fpop();
417: return;
418: case 0xe7:
419: put_longlong_int(PST(0),info,code);
420: fpop();
421: return;
422: }
423: switch (code >> 9) {
424: case 0:
425: get_short_real(&tmp,info,code);
426: break;
427: case 1:
428: get_long_int(&tmp,info,code);
429: break;
430: case 2:
431: get_long_real(&tmp,info,code);
432: break;
433: case 4:
434: get_short_int(&tmp,info,code);
435: }
436: switch ((code>>3) & 0x27) {
437: case 0:
438: fadd(&tmp,PST(0),&tmp);
439: real_to_real(&tmp,&ST(0));
440: return;
441: case 1:
442: fmul(&tmp,PST(0),&tmp);
443: real_to_real(&tmp,&ST(0));
444: return;
445: case 2:
446: fcom(&tmp,PST(0));
447: return;
448: case 3:
449: fcom(&tmp,PST(0));
450: fpop();
451: return;
452: case 4:
453: tmp.exponent ^= 0x8000;
454: fadd(&tmp,PST(0),&tmp);
455: real_to_real(&tmp,&ST(0));
456: return;
457: case 5:
458: ST(0).exponent ^= 0x8000;
459: fadd(&tmp,PST(0),&tmp);
460: real_to_real(&tmp,&ST(0));
461: return;
462: case 6:
463: fdiv(PST(0),&tmp,&tmp);
464: real_to_real(&tmp,&ST(0));
465: return;
466: case 7:
467: fdiv(&tmp,PST(0),&tmp);
468: real_to_real(&tmp,&ST(0));
469: return;
470: }
471: if ((code & 0x138) == 0x100) {
472: fpush();
473: real_to_real(&tmp,&ST(0));
474: return;
475: }
476: printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
477: math_abort(info,1<<(SIGFPE-1));
478: }
479:
480: void math_emulate(long ___false)
481: {
482: if (!current->used_math) {
483: current->used_math = 1;
484: I387.cwd = 0x037f;
485: I387.swd = 0x0000;
486: I387.twd = 0x0000;
487: }
488: do_emu((struct info *) &___false);
489: }
490:
491: void __math_abort(struct info * info, unsigned int signal)
492: {
493: EIP = ORIG_EIP;
494: current->signal |= signal;
495: __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
496: }
497:
498: static void fpop(void)
499: {
500: unsigned long tmp;
501:
502: tmp = I387.swd & 0xffffc7ff;
503: I387.swd += 0x00000800;
504: I387.swd &= 0x00003800;
505: I387.swd |= tmp;
506: }
507:
508: static void fpush(void)
509: {
510: unsigned long tmp;
511:
512: tmp = I387.swd & 0xffffc7ff;
513: I387.swd += 0x00003800;
514: I387.swd &= 0x00003800;
515: I387.swd |= tmp;
516: }
517:
518: static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
519: {
520: temp_real_unaligned c;
521:
522: c = *a;
523: *a = *b;
524: *b = c;
525: }
526:
527: static temp_real_unaligned * __st(int i)
528: {
529: i += I387.swd >> 11;
530: i &= 7;
531: return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
532: }
533:
534: #else /* no math emulation */
535:
536: #include <signal.h>
537: #include <linux/sched.h>
538:
539: void math_emulate(long ___false)
540: {
541: current->signal |= 1<<(SIGFPE-1);
542: schedule();
543: }
544:
545: #endif /* KERNEL_MATH_EMULATION */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.