root/kernel/math/emulate.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. do_emu
  2. math_emulate
  3. __math_abort
  4. fpop
  5. fpush
  6. fxchg
  7. __st
  8. math_emulate

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 540 {
 541         current->signal |= 1<<(SIGFPE-1);
 542         schedule();
 543 }
 544 
 545 #endif /* KERNEL_MATH_EMULATION */

/* [previous][next][first][last][top][bottom][index][help] */