|
|
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.