1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  28  * Use is subject to license terms.
  29  */
  30 
  31 #undef lint
  32 #include <signal.h>
  33 #include <siginfo.h>
  34 #include <ucontext.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <unistd.h>
  38 #include <thread.h>
  39 #include <math.h>
  40 #if defined(__SUNPRO_C)
  41 #include <sunmath.h>
  42 #endif
  43 #include <fenv.h>
  44 #include "fex_handler.h"
  45 #include "fenv_inlines.h"
  46 
  47 #if defined(__sparc) && !defined(__sparcv9)
  48 #include <sys/procfs.h>
  49 #endif
  50 
  51 /*
  52  * 2.x signal.h doesn't declare sigemptyset or sigismember
  53  * if they're #define'd (see sys/signal.h)
  54  */
  55 extern int sigemptyset(sigset_t *);
  56 extern int sigismember(const sigset_t *, int);
  57 
  58 /* external globals */
  59 void (*__mt_fex_sync)() = NULL;         /* for synchronization with libmtsk */
  60 #pragma weak __mt_fex_sync
  61 
  62 void (*__libm_mt_fex_sync)() = NULL;    /* new, improved version of above */
  63 #pragma weak __libm_mt_fex_sync
  64 
  65 /* private variables */
  66 static fex_handler_t main_handlers;
  67 static int handlers_initialized = 0;
  68 static thread_key_t handlers_key;
  69 static mutex_t handlers_key_lock = DEFAULTMUTEX;
  70 static struct sigaction oact = { 0, SIG_DFL };
  71 static mutex_t hdlr_lock = DEFAULTMUTEX;
  72 static int hdlr_installed = 0;
  73 
  74 /* private const data */
  75 static const int te_bit[FEX_NUM_EXC] = {
  76         1 << fp_trap_inexact, 1 << fp_trap_division, 1 << fp_trap_underflow,
  77         1 << fp_trap_overflow, 1 << fp_trap_invalid, 1 << fp_trap_invalid,
  78         1 << fp_trap_invalid, 1 << fp_trap_invalid, 1 << fp_trap_invalid,
  79         1 << fp_trap_invalid, 1 << fp_trap_invalid, 1 << fp_trap_invalid
  80 };
  81 
  82 /*
  83  *  Return the traps to be enabled given the current handling modes
  84  *  and flags
  85  */
  86 static int
  87 __fex_te_needed(struct fex_handler_data *thr_handlers, unsigned long fsr)
  88 {
  89         int i, ex, te;
  90 
  91         /* set traps for handling modes */
  92         te = 0;
  93 
  94         for (i = 0; i < FEX_NUM_EXC; i++)
  95                 if (thr_handlers[i].__mode != FEX_NONSTOP)
  96                         te |= te_bit[i];
  97 
  98         /* add traps for retrospective diagnostics */
  99 
 100         if (fex_get_log()) {
 101                 ex = (int)__fenv_get_ex(fsr);
 102 
 103                 if (!(ex & FE_INEXACT))
 104                         te |= (1 << fp_trap_inexact);
 105 
 106                 if (!(ex & FE_UNDERFLOW))
 107                         te |= (1 << fp_trap_underflow);
 108 
 109                 if (!(ex & FE_OVERFLOW))
 110                         te |= (1 << fp_trap_overflow);
 111 
 112                 if (!(ex & FE_DIVBYZERO))
 113                         te |= (1 << fp_trap_division);
 114 
 115                 if (!(ex & FE_INVALID))
 116                         te |= (1 << fp_trap_invalid);
 117         }
 118 
 119         return (te);
 120 }
 121 
 122 /*
 123  *  The following function synchronizes with libmtsk (SPARC only, for now)
 124  */
 125 static void
 126 __fex_sync_with_libmtsk(int begin, int master)
 127 {
 128         static fenv_t master_env;
 129         static int env_initialized = 0;
 130         static mutex_t env_lock = DEFAULTMUTEX;
 131 
 132         if (begin) {
 133                 mutex_lock(&env_lock);
 134 
 135                 if (master) {
 136                         (void) fegetenv(&master_env);
 137                         env_initialized = 1;
 138                 } else if (env_initialized) {
 139                         (void) fesetenv(&master_env);
 140                 }
 141 
 142                 mutex_unlock(&env_lock);
 143         } else if (master && fex_get_log()) {
 144                 __fex_update_te();
 145         }
 146 }
 147 
 148 /*
 149  *  The following function may be used for synchronization with any
 150  *  internal project that manages multiple threads
 151  */
 152 enum __libm_mt_fex_sync_actions {
 153         __libm_mt_fex_start_master = 0, __libm_mt_fex_start_slave,
 154         __libm_mt_fex_finish_master, __libm_mt_fex_finish_slave
 155 };
 156 
 157 struct __libm_mt_fex_sync_data {
 158         fenv_t master_env;
 159         int initialized;
 160         mutex_t lock;
 161 };
 162 
 163 static void
 164 __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action, struct
 165     __libm_mt_fex_sync_data *thr_env)
 166 {
 167         switch (action) {
 168         case __libm_mt_fex_start_master:
 169                 mutex_lock(&thr_env->lock);
 170                 (void) fegetenv(&thr_env->master_env);
 171                 thr_env->initialized = 1;
 172                 mutex_unlock(&thr_env->lock);
 173                 break;
 174 
 175         case __libm_mt_fex_start_slave:
 176                 mutex_lock(&thr_env->lock);
 177 
 178                 if (thr_env->initialized)
 179                         (void) fesetenv(&thr_env->master_env);
 180 
 181                 mutex_unlock(&thr_env->lock);
 182                 break;
 183 
 184         case __libm_mt_fex_finish_master:
 185 #if defined(__x86)
 186                 __fex_update_te();
 187 #else
 188                 if (fex_get_log())
 189                         __fex_update_te();
 190 #endif
 191                 break;
 192 
 193         case __libm_mt_fex_finish_slave:
 194 #if defined(__x86)
 195                 /*
 196                  * clear traps, making all accrued flags visible in status
 197                  * word
 198                  */
 199                 {
 200                         unsigned long fsr;
 201 
 202                         __fenv_getfsr(&fsr);
 203                         __fenv_set_te(fsr, 0);
 204                         __fenv_setfsr(&fsr);
 205                 }
 206 #endif
 207                 break;
 208         }
 209 }
 210 
 211 #if defined(__sparc)
 212 /*
 213  *  Code for setting or clearing interval mode on US-III and above.
 214  *  This is embedded as data so we don't have to mark the library
 215  *  as a v8plusb/v9b object.  (I could have just used one entry and
 216  *  modified the second word to set the bits I want, but that would
 217  *  have required another mutex.)
 218  */
 219 static const unsigned int siam[][2] = {
 220         { 0x81c3e008, 0x81b01020 },     /* retl, siam 0 */
 221         { 0x81c3e008, 0x81b01024 },     /* retl, siam 4 */
 222         { 0x81c3e008, 0x81b01025 },     /* retl, siam 5 */
 223         { 0x81c3e008, 0x81b01026 },     /* retl, siam 6 */
 224         { 0x81c3e008, 0x81b01027 }      /* retl, siam 7 */
 225 };
 226 
 227 /*
 228  *  If a handling mode is in effect, apply it; otherwise invoke the
 229  *  saved handler
 230  */
 231 static void
 232 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
 233 {
 234         struct fex_handler_data *thr_handlers;
 235         struct sigaction act;
 236 
 237         void (*handler)(), (*siamp)();
 238 
 239         int mode, i;
 240         enum fex_exception e;
 241         fex_info_t info;
 242         unsigned long fsr, tmpfsr, addr;
 243         unsigned int gsr;
 244 
 245         /* determine which exception occurred */
 246         switch (sip->si_code) {
 247         case FPE_FLTDIV:
 248                 e = fex_division;
 249                 break;
 250         case FPE_FLTOVF:
 251                 e = fex_overflow;
 252                 break;
 253         case FPE_FLTUND:
 254                 e = fex_underflow;
 255                 break;
 256         case FPE_FLTRES:
 257                 e = fex_inexact;
 258                 break;
 259         case FPE_FLTINV:
 260 
 261                 if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
 262                         goto not_ieee;
 263 
 264                 break;
 265         default:
 266                 /* not an IEEE exception */
 267                 goto not_ieee;
 268         }
 269 
 270         /* get the handling mode */
 271         mode = FEX_NOHANDLER;
 272         handler = oact.sa_handler; /* for log; just looking, no need to lock */
 273         thr_handlers = __fex_get_thr_handlers();
 274 
 275         if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
 276                 mode = thr_handlers[(int)e].__mode;
 277                 handler = thr_handlers[(int)e].__handler;
 278         }
 279 
 280         /* make an entry in the log of retro. diag. if need be */
 281         i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
 282         __fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
 283 
 284         /* handle the exception based on the mode */
 285         if (mode == FEX_NOHANDLER) {
 286                 goto not_ieee;
 287         } else if (mode == FEX_ABORT) {
 288                 abort();
 289         } else if (mode == FEX_SIGNAL) {
 290                 handler(sig, sip, uap);
 291                 return;
 292         }
 293 
 294         /* custom or nonstop mode; disable traps and clear flags */
 295         __fenv_getfsr(&fsr);
 296         __fenv_set_te(fsr, 0);
 297         __fenv_set_ex(fsr, 0);
 298 
 299         /*
 300          * if interval mode was set, clear it, then substitute the interval
 301          * rounding direction and clear ns mode in the fsr
 302          */
 303 #ifdef __sparcv9
 304         gsr = uap->uc_mcontext.asrs[3];
 305 #else
 306         gsr = 0;
 307 
 308         if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
 309                 gsr = (*(unsigned long long *)
 310                     ((prxregset_t *)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.
 311                     pr_v8p.pr_filler);
 312 #endif
 313         gsr = (gsr >> 25) & 7;
 314 
 315         if (gsr & 4) {
 316                 siamp = (void (*)())siam[0];
 317                 siamp();
 318                 tmpfsr = fsr;
 319                 fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
 320         }
 321 
 322         __fenv_setfsr(&fsr);
 323 
 324         /* decode the operation */
 325         __fex_get_op(sip, uap, &info);
 326 
 327         /* if a custom mode handler is installed, invoke it */
 328         if (mode == FEX_CUSTOM) {
 329                 /* if we got here from feraiseexcept, pass dummy info */
 330                 addr = (unsigned long)sip->si_addr;
 331 
 332                 if (addr >= (unsigned long)feraiseexcept && addr < (unsigned
 333                     long)fetestexcept) {
 334                         info.op = fex_other;
 335                         info.op1.type = info.op2.type = info.res.type =
 336                             fex_nodata;
 337                 }
 338 
 339                 /*
 340                  * restore interval mode if it was set, and put the original
 341                  * rounding direction and ns mode back in the fsr
 342                  */
 343                 if (gsr & 4) {
 344                         __fenv_setfsr(&tmpfsr);
 345                         siamp = (void (*)())siam[1 + (gsr & 3)];
 346                         siamp();
 347                 }
 348 
 349                 handler(1 << (int)e, &info);
 350 
 351                 /* restore modes in case the user's handler changed them */
 352                 if (gsr & 4) {
 353                         siamp = (void (*)())siam[0];
 354                         siamp();
 355                 }
 356 
 357                 __fenv_setfsr(&fsr);
 358         }
 359 
 360         /* stuff the result */
 361         __fex_st_result(sip, uap, &info);
 362 
 363         /* "or" in any exception flags and update traps */
 364         fsr = uap->uc_mcontext.fpregs.fpu_fsr;
 365         fsr |= ((info.flags & 0x1f) << 5);
 366         i = __fex_te_needed(thr_handlers, fsr);
 367         __fenv_set_te(fsr, i);
 368         uap->uc_mcontext.fpregs.fpu_fsr = fsr;
 369         return;
 370 
 371 not_ieee:
 372         /* revert to the saved handler (if any) */
 373         mutex_lock(&hdlr_lock);
 374         act = oact;
 375         mutex_unlock(&hdlr_lock);
 376 
 377         switch ((unsigned long)act.sa_handler) {
 378         case (unsigned long)SIG_DFL:
 379                 /* simulate trap with no handler installed */
 380                 sigaction(SIGFPE, &act, NULL);
 381                 kill(getpid(), SIGFPE);
 382                 break;
 383 #if !defined(__lint)
 384         case (unsigned long)SIG_IGN:
 385                 break;
 386 #endif
 387         default:
 388                 act.sa_handler(sig, sip, uap);
 389         }
 390 }
 391 #elif defined(__x86)
 392 #if defined(__amd64)
 393 #define test_sse_hw             1
 394 #else
 395 extern int _sse_hw;
 396 
 397 #define test_sse_hw             _sse_hw
 398 #endif
 399 
 400 #if !defined(REG_PC)
 401 #define REG_PC                  EIP
 402 #endif
 403 
 404 /*
 405  *  If a handling mode is in effect, apply it; otherwise invoke the
 406  *  saved handler
 407  */
 408 static void
 409 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
 410 {
 411         struct fex_handler_data *thr_handlers;
 412         struct sigaction act;
 413 
 414         void (*handler)() = NULL, (*simd_handler[4])();
 415 
 416         int mode, simd_mode[4], i, len, accrued, *ap;
 417         unsigned int cwsw, oldcwsw, mxcsr, oldmxcsr;
 418         enum fex_exception e, simd_e[4];
 419         fex_info_t info, simd_info[4];
 420         unsigned long addr;
 421         siginfo_t osip = *sip;
 422         sseinst_t inst;
 423 
 424         /* check for an exception caused by an SSE instruction */
 425         if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
 426                 len = __fex_parse_sse(uap, &inst);
 427 
 428                 if (len == 0)
 429                         goto not_ieee;
 430 
 431                 /* disable all traps and clear flags */
 432                 __fenv_getcwsw(&oldcwsw);
 433                 cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
 434                 __fenv_setcwsw(&cwsw);
 435                 __fenv_getmxcsr(&oldmxcsr);
 436                 mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
 437                 __fenv_setmxcsr(&mxcsr);
 438 
 439                 if ((int)inst.op & SIMD) {
 440                         __fex_get_simd_op(uap, &inst, simd_e, simd_info);
 441 
 442                         thr_handlers = __fex_get_thr_handlers();
 443                         addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
 444                         accrued =
 445                             uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 446                             mxcsr;
 447 
 448                         e = (enum fex_exception)-1;
 449                         mode = FEX_NONSTOP;
 450 
 451                         for (i = 0; i < 4; i++) {
 452                                 if ((int)simd_e[i] < 0)
 453                                         continue;
 454 
 455                                 e = simd_e[i];
 456                                 simd_mode[i] = FEX_NOHANDLER;
 457                                 simd_handler[i] = oact.sa_handler;
 458 
 459                                 if (thr_handlers &&
 460                                     thr_handlers[(int)e].__mode !=
 461                                     FEX_NOHANDLER) {
 462                                         simd_mode[i] =
 463                                             thr_handlers[(int)e].__mode;
 464                                         simd_handler[i] =
 465                                             thr_handlers[(int)e].__handler;
 466                                 }
 467 
 468                                 accrued &= ~te_bit[(int)e];
 469 
 470                                 switch (simd_mode[i]) {
 471                                 case FEX_ABORT:
 472                                         mode = FEX_ABORT;
 473                                         break;
 474                                 case FEX_SIGNAL:
 475 
 476                                         if (mode != FEX_ABORT)
 477                                                 mode = FEX_SIGNAL;
 478 
 479                                         handler = simd_handler[i];
 480                                         break;
 481                                 case FEX_NOHANDLER:
 482 
 483                                         if (mode != FEX_ABORT && mode !=
 484                                             FEX_SIGNAL)
 485                                                 mode = FEX_NOHANDLER;
 486 
 487                                         break;
 488                                 }
 489                         }
 490 
 491                         if (e == (enum fex_exception)-1) {
 492                                 __fenv_setcwsw(&oldcwsw);
 493                                 __fenv_setmxcsr(&oldmxcsr);
 494                                 goto not_ieee;
 495                         }
 496 
 497                         accrued |=
 498                             uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 499                             status;
 500                         ap = __fex_accrued();
 501                         accrued |= *ap;
 502                         accrued &= 0x3d;
 503 
 504                         for (i = 0; i < 4; i++) {
 505                                 if ((int)simd_e[i] < 0)
 506                                         continue;
 507 
 508                                 __fex_mklog(uap, (char *)addr, accrued,
 509                                     simd_e[i], simd_mode[i],
 510                                     (void *)simd_handler[i]);
 511                         }
 512 
 513                         if (mode == FEX_NOHANDLER) {
 514                                 __fenv_setcwsw(&oldcwsw);
 515                                 __fenv_setmxcsr(&oldmxcsr);
 516                                 goto not_ieee;
 517                         } else if (mode == FEX_ABORT) {
 518                                 abort();
 519                         } else if (mode == FEX_SIGNAL) {
 520                                 __fenv_setcwsw(&oldcwsw);
 521                                 __fenv_setmxcsr(&oldmxcsr);
 522                                 handler(sig, &osip, uap);
 523                                 return;
 524                         }
 525 
 526                         *ap = 0;
 527 
 528                         for (i = 0; i < 4; i++) {
 529                                 if ((int)simd_e[i] < 0)
 530                                         continue;
 531 
 532                                 if (simd_mode[i] == FEX_CUSTOM) {
 533                                         handler(1 << (int)simd_e[i],
 534                                             &simd_info[i]);
 535                                         __fenv_setcwsw(&cwsw);
 536                                         __fenv_setmxcsr(&mxcsr);
 537                                 }
 538                         }
 539 
 540                         __fex_st_simd_result(uap, &inst, simd_e, simd_info);
 541 
 542                         for (i = 0; i < 4; i++) {
 543                                 if ((int)simd_e[i] < 0)
 544                                         continue;
 545 
 546                                 accrued |= simd_info[i].flags;
 547                         }
 548 
 549                         if ((int)inst.op & INTREG) {
 550                                 /* set MMX mode */
 551 #if defined(__amd64)
 552                                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 553                                     sw &= ~0x3800;
 554                                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 555                                     fctw = 0;
 556 #else
 557                                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 558                                     state[1] &= ~0x3800;
 559                                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 560                                     state[2] = 0;
 561 #endif
 562                         }
 563                 } else {
 564                         e = __fex_get_sse_op(uap, &inst, &info);
 565 
 566                         if ((int)e < 0) {
 567                                 __fenv_setcwsw(&oldcwsw);
 568                                 __fenv_setmxcsr(&oldmxcsr);
 569                                 goto not_ieee;
 570                         }
 571 
 572                         mode = FEX_NOHANDLER;
 573                         handler = oact.sa_handler;
 574                         thr_handlers = __fex_get_thr_handlers();
 575 
 576                         if (thr_handlers && thr_handlers[(int)e].__mode !=
 577                             FEX_NOHANDLER) {
 578                                 mode = thr_handlers[(int)e].__mode;
 579                                 handler = thr_handlers[(int)e].__handler;
 580                         }
 581 
 582                         addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
 583                         accrued =
 584                             uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 585                             mxcsr & ~te_bit[(int)e];
 586                         accrued |=
 587                             uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
 588                             status;
 589                         ap = __fex_accrued();
 590                         accrued |= *ap;
 591                         accrued &= 0x3d;
 592                         __fex_mklog(uap, (char *)addr, accrued, e, mode,
 593                             (void *)handler);
 594 
 595                         if (mode == FEX_NOHANDLER) {
 596                                 __fenv_setcwsw(&oldcwsw);
 597                                 __fenv_setmxcsr(&oldmxcsr);
 598                                 goto not_ieee;
 599                         } else if (mode == FEX_ABORT) {
 600                                 abort();
 601                         } else if (mode == FEX_SIGNAL) {
 602                                 __fenv_setcwsw(&oldcwsw);
 603                                 __fenv_setmxcsr(&oldmxcsr);
 604                                 handler(sig, &osip, uap);
 605                                 return;
 606                         } else if (mode == FEX_CUSTOM) {
 607                                 *ap = 0;
 608 
 609                                 if (addr >= (unsigned long)feraiseexcept &&
 610                                     addr < (unsigned long)fetestexcept) {
 611                                         info.op = fex_other;
 612                                         info.op1.type = info.op2.type =
 613                                             info.res.type = fex_nodata;
 614                                 }
 615 
 616                                 handler(1 << (int)e, &info);
 617                                 __fenv_setcwsw(&cwsw);
 618                                 __fenv_setmxcsr(&mxcsr);
 619                         }
 620 
 621                         __fex_st_sse_result(uap, &inst, e, &info);
 622                         accrued |= info.flags;
 623 
 624 #if defined(__amd64)
 625                         /*
 626                          * In 64-bit mode, the 32-bit convert-to-integer
 627                          * instructions zero the upper 32 bits of the
 628                          * destination.  (We do this here and not in
 629                          * __fex_st_sse_result because __fex_st_sse_result
 630                          * can be called from __fex_st_simd_result, too.)
 631                          */
 632                         if (inst.op == cvtss2si || inst.op == cvttss2si ||
 633                             inst.op == cvtsd2si || inst.op == cvttsd2si)
 634                                 inst.op1->i[1] = 0;
 635 #endif
 636                 }
 637 
 638                 /* advance the pc past the SSE instruction */
 639                 uap->uc_mcontext.gregs[REG_PC] += len;
 640                 goto update_state;
 641         }
 642 
 643         /* determine which exception occurred */
 644         __fex_get_x86_exc(sip, uap);
 645 
 646         switch (sip->si_code) {
 647         case FPE_FLTDIV:
 648                 e = fex_division;
 649                 break;
 650         case FPE_FLTOVF:
 651                 e = fex_overflow;
 652                 break;
 653         case FPE_FLTUND:
 654                 e = fex_underflow;
 655                 break;
 656         case FPE_FLTRES:
 657                 e = fex_inexact;
 658                 break;
 659         case FPE_FLTINV:
 660 
 661                 if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
 662                         goto not_ieee;
 663 
 664                 break;
 665         default:
 666                 /* not an IEEE exception */
 667                 goto not_ieee;
 668         }
 669 
 670         /* get the handling mode */
 671         mode = FEX_NOHANDLER;
 672         handler = oact.sa_handler; /* for log; just looking, no need to lock */
 673         thr_handlers = __fex_get_thr_handlers();
 674 
 675         if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
 676                 mode = thr_handlers[(int)e].__mode;
 677                 handler = thr_handlers[(int)e].__handler;
 678         }
 679 
 680         /* make an entry in the log of retro. diag. if need be */
 681 #if defined(__amd64)
 682         addr = (unsigned
 683             long)uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rip;
 684 #else
 685         addr = (unsigned
 686             long)uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[3];
 687 #endif
 688         accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status &
 689             ~te_bit[(int)e];
 690 
 691         if (test_sse_hw)
 692                 accrued |=
 693                     uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr;
 694 
 695         ap = __fex_accrued();
 696         accrued |= *ap;
 697         accrued &= 0x3d;
 698         __fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
 699 
 700         /* handle the exception based on the mode */
 701         if (mode == FEX_NOHANDLER) {
 702                 goto not_ieee;
 703         } else if (mode == FEX_ABORT) {
 704                 abort();
 705         } else if (mode == FEX_SIGNAL) {
 706                 handler(sig, &osip, uap);
 707                 return;
 708         }
 709 
 710         /* disable all traps and clear flags */
 711         __fenv_getcwsw(&cwsw);
 712         cwsw = (cwsw & ~0x3f) | 0x003f0000;
 713         __fenv_setcwsw(&cwsw);
 714 
 715         if (test_sse_hw) {
 716                 __fenv_getmxcsr(&mxcsr);
 717                 mxcsr = (mxcsr & ~0x3f) | 0x1f80;
 718                 __fenv_setmxcsr(&mxcsr);
 719         }
 720 
 721         *ap = 0;
 722 
 723         /* decode the operation */
 724         __fex_get_op(sip, uap, &info);
 725 
 726         /* if a custom mode handler is installed, invoke it */
 727         if (mode == FEX_CUSTOM) {
 728                 /* if we got here from feraiseexcept, pass dummy info */
 729                 if (addr >= (unsigned long)feraiseexcept && addr < (unsigned
 730                     long)fetestexcept) {
 731                         info.op = fex_other;
 732                         info.op1.type = info.op2.type = info.res.type =
 733                             fex_nodata;
 734                 }
 735 
 736                 handler(1 << (int)e, &info);
 737 
 738                 /* restore modes in case the user's handler changed them */
 739                 __fenv_setcwsw(&cwsw);
 740 
 741                 if (test_sse_hw)
 742                         __fenv_setmxcsr(&mxcsr);
 743         }
 744 
 745         /* stuff the result */
 746         __fex_st_result(sip, uap, &info);
 747         accrued |= info.flags;
 748 
 749 update_state:
 750         accrued &= 0x3d;
 751         i = __fex_te_needed(thr_handlers, accrued);
 752         *ap = accrued & i;
 753 #if defined(__amd64)
 754         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
 755         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
 756         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
 757         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
 758 #else
 759         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
 760         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |= (accrued &
 761             ~i);
 762         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
 763         uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
 764 #endif
 765 
 766         if (test_sse_hw) {
 767                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
 768                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
 769                     0x1e80 | (accrued & ~i);
 770                 uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~(i <<
 771                     7);
 772         }
 773 
 774         return;
 775 
 776 not_ieee:
 777         /* revert to the saved handler (if any) */
 778         mutex_lock(&hdlr_lock);
 779         act = oact;
 780         mutex_unlock(&hdlr_lock);
 781 
 782         switch ((unsigned long)act.sa_handler) {
 783         case (unsigned long)SIG_DFL:
 784                 /* simulate trap with no handler installed */
 785                 sigaction(SIGFPE, &act, NULL);
 786                 kill(getpid(), SIGFPE);
 787                 break;
 788 #if !defined(__lint)
 789         case (unsigned long)SIG_IGN:
 790                 break;
 791 #endif
 792         default:
 793                 act.sa_handler(sig, &osip, uap);
 794         }
 795 }
 796 #else
 797 #error Unknown architecture
 798 #endif
 799 
 800 /*
 801  *  Return a pointer to the thread-specific handler data, and
 802  *  initialize it if necessary
 803  */
 804 struct fex_handler_data *
 805 __fex_get_thr_handlers()
 806 {
 807         struct fex_handler_data *ptr;
 808         unsigned long fsr;
 809         int i, te;
 810 
 811         if (thr_main()) {
 812                 if (!handlers_initialized) {
 813                         /*
 814                          * initialize to FEX_NOHANDLER if trap is enabled,
 815                          * FEX_NONSTOP if trap is disabled
 816                          */
 817                         __fenv_getfsr(&fsr);
 818                         te = (int)__fenv_get_te(fsr);
 819 
 820                         for (i = 0; i < FEX_NUM_EXC; i++)
 821                                 main_handlers[i].__mode = ((te & te_bit[i]) ?
 822                                     FEX_NOHANDLER : FEX_NONSTOP);
 823 
 824                         handlers_initialized = 1;
 825                 }
 826 
 827                 return (main_handlers);
 828         } else {
 829                 ptr = NULL;
 830                 mutex_lock(&handlers_key_lock);
 831 
 832                 if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
 833                     thr_keycreate(&handlers_key, free) != 0) {
 834                         mutex_unlock(&handlers_key_lock);
 835                         return (NULL);
 836                 }
 837 
 838                 mutex_unlock(&handlers_key_lock);
 839 
 840                 if (!ptr) {
 841                         if ((ptr = malloc(sizeof (fex_handler_t))) == NULL)
 842                                 return (NULL);
 843 
 844                         if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
 845                                 (void) free(ptr);
 846                                 return (NULL);
 847                         }
 848 
 849                         /*
 850                          * initialize to FEX_NOHANDLER if trap is enabled,
 851                          * FEX_NONSTOP if trap is disabled
 852                          */
 853                         __fenv_getfsr(&fsr);
 854                         te = (int)__fenv_get_te(fsr);
 855 
 856                         for (i = 0; i < FEX_NUM_EXC; i++)
 857                                 ptr[i].__mode = ((te & te_bit[i]) ?
 858                                     FEX_NOHANDLER : FEX_NONSTOP);
 859                 }
 860 
 861                 return (ptr);
 862         }
 863 }
 864 
 865 /*
 866  *  Update the trap enable bits according to the selected modes
 867  */
 868 void
 869 __fex_update_te()
 870 {
 871         struct fex_handler_data *thr_handlers;
 872         struct sigaction act, tmpact;
 873         sigset_t blocked;
 874         unsigned long fsr;
 875         int te;
 876 
 877         /* determine which traps are needed */
 878         thr_handlers = __fex_get_thr_handlers();
 879         __fenv_getfsr(&fsr);
 880         te = __fex_te_needed(thr_handlers, fsr);
 881 
 882         /* install __fex_hdlr as necessary */
 883         if (!hdlr_installed && te) {
 884                 act.sa_handler = __fex_hdlr;
 885                 sigemptyset(&act.sa_mask);
 886                 act.sa_flags = SA_SIGINFO;
 887                 sigaction(SIGFPE, &act, &tmpact);
 888 
 889                 if (tmpact.sa_handler != __fex_hdlr) {
 890                         mutex_lock(&hdlr_lock);
 891                         oact = tmpact;
 892                         mutex_unlock(&hdlr_lock);
 893                 }
 894 
 895                 hdlr_installed = 1;
 896         }
 897 
 898         /* set the new trap enable bits (only if SIGFPE is not blocked) */
 899         if (sigprocmask(0, NULL, &blocked) == 0 && !sigismember(&blocked,
 900             SIGFPE)) {
 901                 __fenv_set_te(fsr, te);
 902                 __fenv_setfsr(&fsr);
 903         }
 904 
 905         /* synchronize with libmtsk */
 906         __mt_fex_sync = __fex_sync_with_libmtsk;
 907 
 908         /* synchronize with other projects */
 909         __libm_mt_fex_sync = __fex_sync_with_threads;
 910 }