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