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.
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.
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]
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
40 #include "fex_handler.h"
41 #include "fenv_inlines.h"
43 #if defined(__sparc) && !defined(__sparcv9)
44 #include <sys/procfs.h>
47 /* 2.x signal.h doesn't declare sigemptyset or sigismember
48 if they're #defined (see sys/signal.h) */
49 extern int sigemptyset(sigset_t
*);
50 extern int sigismember(const sigset_t
*, int);
52 /* external globals */
53 void (*__mt_fex_sync
)() = NULL
; /* for synchronization with libmtsk */
54 #pragma weak __mt_fex_sync
56 void (*__libm_mt_fex_sync
)() = NULL
; /* new, improved version of above */
57 #pragma weak __libm_mt_fex_sync
59 /* private variables */
60 static fex_handler_t main_handlers
;
61 static int handlers_initialized
= 0;
62 static thread_key_t handlers_key
;
63 static mutex_t handlers_key_lock
= DEFAULTMUTEX
;
65 static struct sigaction oact
= { 0, SIG_DFL
};
66 static mutex_t hdlr_lock
= DEFAULTMUTEX
;
67 static int hdlr_installed
= 0;
69 /* private const data */
70 static const int te_bit
[FEX_NUM_EXC
] = {
72 1 << fp_trap_division
,
73 1 << fp_trap_underflow
,
74 1 << fp_trap_overflow
,
86 * Return the traps to be enabled given the current handling modes
90 __fex_te_needed(struct fex_handler_data
*thr_handlers
, unsigned long fsr
)
94 /* set traps for handling modes */
96 for (i
= 0; i
< FEX_NUM_EXC
; i
++)
97 if (thr_handlers
[i
].__mode
!= FEX_NONSTOP
)
100 /* add traps for retrospective diagnostics */
102 ex
= (int)__fenv_get_ex(fsr
);
103 if (!(ex
& FE_INEXACT
))
104 te
|= (1 << fp_trap_inexact
);
105 if (!(ex
& FE_UNDERFLOW
))
106 te
|= (1 << fp_trap_underflow
);
107 if (!(ex
& FE_OVERFLOW
))
108 te
|= (1 << fp_trap_overflow
);
109 if (!(ex
& FE_DIVBYZERO
))
110 te
|= (1 << fp_trap_division
);
111 if (!(ex
& FE_INVALID
))
112 te
|= (1 << fp_trap_invalid
);
119 * The following function synchronizes with libmtsk (SPARC only, for now)
122 __fex_sync_with_libmtsk(int begin
, int master
)
124 static fenv_t master_env
;
125 static int env_initialized
= 0;
126 static mutex_t env_lock
= DEFAULTMUTEX
;
129 mutex_lock(&env_lock
);
131 (void) fegetenv(&master_env
);
134 else if (env_initialized
)
135 (void) fesetenv(&master_env
);
136 mutex_unlock(&env_lock
);
138 else if (master
&& fex_get_log())
143 * The following function may be used for synchronization with any
144 * internal project that manages multiple threads
146 enum __libm_mt_fex_sync_actions
{
147 __libm_mt_fex_start_master
= 0,
148 __libm_mt_fex_start_slave
,
149 __libm_mt_fex_finish_master
,
150 __libm_mt_fex_finish_slave
153 struct __libm_mt_fex_sync_data
{
160 __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action
,
161 struct __libm_mt_fex_sync_data
*thr_env
)
164 case __libm_mt_fex_start_master
:
165 mutex_lock(&thr_env
->lock
);
166 (void) fegetenv(&thr_env
->master_env
);
167 thr_env
->initialized
= 1;
168 mutex_unlock(&thr_env
->lock
);
171 case __libm_mt_fex_start_slave
:
172 mutex_lock(&thr_env
->lock
);
173 if (thr_env
->initialized
)
174 (void) fesetenv(&thr_env
->master_env
);
175 mutex_unlock(&thr_env
->lock
);
178 case __libm_mt_fex_finish_master
:
187 case __libm_mt_fex_finish_slave
:
189 /* clear traps, making all accrued flags visible in status word */
193 __fenv_set_te(fsr
, 0);
204 * Code for setting or clearing interval mode on US-III and above.
205 * This is embedded as data so we don't have to mark the library
206 * as a v8plusb/v9b object. (I could have just used one entry and
207 * modified the second word to set the bits I want, but that would
208 * have required another mutex.)
210 static const unsigned int siam
[][2] = {
211 { 0x81c3e008, 0x81b01020 }, /* retl, siam 0 */
212 { 0x81c3e008, 0x81b01024 }, /* retl, siam 4 */
213 { 0x81c3e008, 0x81b01025 }, /* retl, siam 5 */
214 { 0x81c3e008, 0x81b01026 }, /* retl, siam 6 */
215 { 0x81c3e008, 0x81b01027 } /* retl, siam 7 */
219 * If a handling mode is in effect, apply it; otherwise invoke the
223 __fex_hdlr(int sig
, siginfo_t
*sip
, ucontext_t
*uap
)
225 struct fex_handler_data
*thr_handlers
;
226 struct sigaction act
;
227 void (*handler
)(), (*siamp
)();
229 enum fex_exception e
;
231 unsigned long fsr
, tmpfsr
, addr
;
234 /* determine which exception occurred */
235 switch (sip
->si_code
) {
249 if ((int)(e
= __fex_get_invalid_type(sip
, uap
)) < 0)
253 /* not an IEEE exception */
257 /* get the handling mode */
258 mode
= FEX_NOHANDLER
;
259 handler
= oact
.sa_handler
; /* for log; just looking, no need to lock */
260 thr_handlers
= __fex_get_thr_handlers();
261 if (thr_handlers
&& thr_handlers
[(int)e
].__mode
!= FEX_NOHANDLER
) {
262 mode
= thr_handlers
[(int)e
].__mode
;
263 handler
= thr_handlers
[(int)e
].__handler
;
266 /* make an entry in the log of retro. diag. if need be */
267 i
= ((int)uap
->uc_mcontext
.fpregs
.fpu_fsr
>> 5) & 0x1f;
268 __fex_mklog(uap
, (char *)sip
->si_addr
, i
, e
, mode
, (void *)handler
);
270 /* handle the exception based on the mode */
271 if (mode
== FEX_NOHANDLER
)
273 else if (mode
== FEX_ABORT
)
275 else if (mode
== FEX_SIGNAL
) {
276 handler(sig
, sip
, uap
);
280 /* custom or nonstop mode; disable traps and clear flags */
282 __fenv_set_te(fsr
, 0);
283 __fenv_set_ex(fsr
, 0);
285 /* if interval mode was set, clear it, then substitute the
286 interval rounding direction and clear ns mode in the fsr */
288 gsr
= uap
->uc_mcontext
.asrs
[3];
291 if (uap
->uc_mcontext
.xrs
.xrs_id
== XRS_ID
)
292 gsr
= (*(unsigned long long*)((prxregset_t
*)uap
->uc_mcontext
.
293 xrs
.xrs_ptr
)->pr_un
.pr_v8p
.pr_filler
);
295 gsr
= (gsr
>> 25) & 7;
297 siamp
= (void (*)()) siam
[0];
300 fsr
= (fsr
& ~0xc0400000ul
) | ((gsr
& 3) << 30);
304 /* decode the operation */
305 __fex_get_op(sip
, uap
, &info
);
307 /* if a custom mode handler is installed, invoke it */
308 if (mode
== FEX_CUSTOM
) {
309 /* if we got here from feraiseexcept, pass dummy info */
310 addr
= (unsigned long)sip
->si_addr
;
311 if (addr
>= (unsigned long)feraiseexcept
&&
312 addr
< (unsigned long)fetestexcept
) {
314 info
.op1
.type
= info
.op2
.type
= info
.res
.type
=
318 /* restore interval mode if it was set, and put the original
319 rounding direction and ns mode back in the fsr */
321 __fenv_setfsr(&tmpfsr
);
322 siamp
= (void (*)()) siam
[1 + (gsr
& 3)];
326 handler(1 << (int)e
, &info
);
328 /* restore modes in case the user's handler changed them */
330 siamp
= (void (*)()) siam
[0];
336 /* stuff the result */
337 __fex_st_result(sip
, uap
, &info
);
339 /* "or" in any exception flags and update traps */
340 fsr
= uap
->uc_mcontext
.fpregs
.fpu_fsr
;
341 fsr
|= ((info
.flags
& 0x1f) << 5);
342 i
= __fex_te_needed(thr_handlers
, fsr
);
343 __fenv_set_te(fsr
, i
);
344 uap
->uc_mcontext
.fpregs
.fpu_fsr
= fsr
;
348 /* revert to the saved handler (if any) */
349 mutex_lock(&hdlr_lock
);
351 mutex_unlock(&hdlr_lock
);
352 switch ((unsigned long)act
.sa_handler
) {
353 case (unsigned long)SIG_DFL
:
354 /* simulate trap with no handler installed */
355 sigaction(SIGFPE
, &act
, NULL
);
356 kill(getpid(), SIGFPE
);
358 case (unsigned long)SIG_IGN
:
361 act
.sa_handler(sig
, sip
, uap
);
368 #define test_sse_hw 1
371 #define test_sse_hw _sse_hw
379 * If a handling mode is in effect, apply it; otherwise invoke the
383 __fex_hdlr(int sig
, siginfo_t
*sip
, ucontext_t
*uap
)
385 struct fex_handler_data
*thr_handlers
;
386 struct sigaction act
;
387 void (*handler
)() = NULL
, (*simd_handler
[4])();
388 int mode
, simd_mode
[4], i
, len
, accrued
, *ap
;
389 unsigned int cwsw
, oldcwsw
, mxcsr
, oldmxcsr
;
390 enum fex_exception e
, simd_e
[4];
391 fex_info_t info
, simd_info
[4];
393 siginfo_t osip
= *sip
;
396 /* check for an exception caused by an SSE instruction */
397 if (!(uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.status
& 0x80)) {
398 len
= __fex_parse_sse(uap
, &inst
);
402 /* disable all traps and clear flags */
403 __fenv_getcwsw(&oldcwsw
);
404 cwsw
= (oldcwsw
& ~0x3f) | 0x003f0000;
405 __fenv_setcwsw(&cwsw
);
406 __fenv_getmxcsr(&oldmxcsr
);
407 mxcsr
= (oldmxcsr
& ~0x3f) | 0x1f80;
408 __fenv_setmxcsr(&mxcsr
);
410 if ((int)inst
.op
& SIMD
) {
411 __fex_get_simd_op(uap
, &inst
, simd_e
, simd_info
);
413 thr_handlers
= __fex_get_thr_handlers();
414 addr
= (unsigned long)uap
->uc_mcontext
.gregs
[REG_PC
];
415 accrued
= uap
->uc_mcontext
.fpregs
.fp_reg_set
.
418 e
= (enum fex_exception
)-1;
420 for (i
= 0; i
< 4; i
++) {
421 if ((int)simd_e
[i
] < 0)
425 simd_mode
[i
] = FEX_NOHANDLER
;
426 simd_handler
[i
] = oact
.sa_handler
;
428 thr_handlers
[(int)e
].__mode
!=
431 thr_handlers
[(int)e
].__mode
;
433 thr_handlers
[(int)e
].__handler
;
435 accrued
&= ~te_bit
[(int)e
];
436 switch (simd_mode
[i
]) {
441 if (mode
!= FEX_ABORT
)
443 handler
= simd_handler
[i
];
446 if (mode
!= FEX_ABORT
&& mode
!=
448 mode
= FEX_NOHANDLER
;
452 if (e
== (enum fex_exception
)-1) {
453 __fenv_setcwsw(&oldcwsw
);
454 __fenv_setmxcsr(&oldmxcsr
);
457 accrued
|= uap
->uc_mcontext
.fpregs
.fp_reg_set
.
459 ap
= __fex_accrued();
463 for (i
= 0; i
< 4; i
++) {
464 if ((int)simd_e
[i
] < 0)
467 __fex_mklog(uap
, (char *)addr
, accrued
,
468 simd_e
[i
], simd_mode
[i
],
469 (void *)simd_handler
[i
]);
472 if (mode
== FEX_NOHANDLER
) {
473 __fenv_setcwsw(&oldcwsw
);
474 __fenv_setmxcsr(&oldmxcsr
);
476 } else if (mode
== FEX_ABORT
) {
478 } else if (mode
== FEX_SIGNAL
) {
479 __fenv_setcwsw(&oldcwsw
);
480 __fenv_setmxcsr(&oldmxcsr
);
481 handler(sig
, &osip
, uap
);
486 for (i
= 0; i
< 4; i
++) {
487 if ((int)simd_e
[i
] < 0)
490 if (simd_mode
[i
] == FEX_CUSTOM
) {
491 handler(1 << (int)simd_e
[i
],
493 __fenv_setcwsw(&cwsw
);
494 __fenv_setmxcsr(&mxcsr
);
498 __fex_st_simd_result(uap
, &inst
, simd_e
, simd_info
);
499 for (i
= 0; i
< 4; i
++) {
500 if ((int)simd_e
[i
] < 0)
503 accrued
|= simd_info
[i
].flags
;
506 if ((int)inst
.op
& INTREG
) {
509 uap
->uc_mcontext
.fpregs
.fp_reg_set
.
510 fpchip_state
.sw
&= ~0x3800;
511 uap
->uc_mcontext
.fpregs
.fp_reg_set
.
512 fpchip_state
.fctw
= 0;
514 uap
->uc_mcontext
.fpregs
.fp_reg_set
.
515 fpchip_state
.state
[1] &= ~0x3800;
516 uap
->uc_mcontext
.fpregs
.fp_reg_set
.
517 fpchip_state
.state
[2] = 0;
521 e
= __fex_get_sse_op(uap
, &inst
, &info
);
523 __fenv_setcwsw(&oldcwsw
);
524 __fenv_setmxcsr(&oldmxcsr
);
528 mode
= FEX_NOHANDLER
;
529 handler
= oact
.sa_handler
;
530 thr_handlers
= __fex_get_thr_handlers();
531 if (thr_handlers
&& thr_handlers
[(int)e
].__mode
!=
533 mode
= thr_handlers
[(int)e
].__mode
;
534 handler
= thr_handlers
[(int)e
].__handler
;
537 addr
= (unsigned long)uap
->uc_mcontext
.gregs
[REG_PC
];
538 accrued
= uap
->uc_mcontext
.fpregs
.fp_reg_set
.
539 fpchip_state
.mxcsr
& ~te_bit
[(int)e
];
540 accrued
|= uap
->uc_mcontext
.fpregs
.fp_reg_set
.
542 ap
= __fex_accrued();
545 __fex_mklog(uap
, (char *)addr
, accrued
, e
, mode
,
548 if (mode
== FEX_NOHANDLER
) {
549 __fenv_setcwsw(&oldcwsw
);
550 __fenv_setmxcsr(&oldmxcsr
);
552 } else if (mode
== FEX_ABORT
) {
554 } else if (mode
== FEX_SIGNAL
) {
555 __fenv_setcwsw(&oldcwsw
);
556 __fenv_setmxcsr(&oldmxcsr
);
557 handler(sig
, &osip
, uap
);
559 } else if (mode
== FEX_CUSTOM
) {
561 if (addr
>= (unsigned long)feraiseexcept
&&
562 addr
< (unsigned long)fetestexcept
) {
564 info
.op1
.type
= info
.op2
.type
=
565 info
.res
.type
= fex_nodata
;
567 handler(1 << (int)e
, &info
);
568 __fenv_setcwsw(&cwsw
);
569 __fenv_setmxcsr(&mxcsr
);
572 __fex_st_sse_result(uap
, &inst
, e
, &info
);
573 accrued
|= info
.flags
;
577 * In 64-bit mode, the 32-bit convert-to-integer
578 * instructions zero the upper 32 bits of the
579 * destination. (We do this here and not in
580 * __fex_st_sse_result because __fex_st_sse_result
581 * can be called from __fex_st_simd_result, too.)
583 if (inst
.op
== cvtss2si
|| inst
.op
== cvttss2si
||
584 inst
.op
== cvtsd2si
|| inst
.op
== cvttsd2si
)
589 /* advance the pc past the SSE instruction */
590 uap
->uc_mcontext
.gregs
[REG_PC
] += len
;
594 /* determine which exception occurred */
595 __fex_get_x86_exc(sip
, uap
);
596 switch (sip
->si_code
) {
610 if ((int)(e
= __fex_get_invalid_type(sip
, uap
)) < 0)
614 /* not an IEEE exception */
618 /* get the handling mode */
619 mode
= FEX_NOHANDLER
;
620 handler
= oact
.sa_handler
; /* for log; just looking, no need to lock */
621 thr_handlers
= __fex_get_thr_handlers();
622 if (thr_handlers
&& thr_handlers
[(int)e
].__mode
!= FEX_NOHANDLER
) {
623 mode
= thr_handlers
[(int)e
].__mode
;
624 handler
= thr_handlers
[(int)e
].__handler
;
627 /* make an entry in the log of retro. diag. if need be */
629 addr
= (unsigned long)uap
->uc_mcontext
.fpregs
.fp_reg_set
.
632 addr
= (unsigned long)uap
->uc_mcontext
.fpregs
.fp_reg_set
.
633 fpchip_state
.state
[3];
635 accrued
= uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.status
&
638 accrued
|= uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.
640 ap
= __fex_accrued();
643 __fex_mklog(uap
, (char *)addr
, accrued
, e
, mode
, (void *)handler
);
645 /* handle the exception based on the mode */
646 if (mode
== FEX_NOHANDLER
)
648 else if (mode
== FEX_ABORT
)
650 else if (mode
== FEX_SIGNAL
) {
651 handler(sig
, &osip
, uap
);
655 /* disable all traps and clear flags */
656 __fenv_getcwsw(&cwsw
);
657 cwsw
= (cwsw
& ~0x3f) | 0x003f0000;
658 __fenv_setcwsw(&cwsw
);
660 __fenv_getmxcsr(&mxcsr
);
661 mxcsr
= (mxcsr
& ~0x3f) | 0x1f80;
662 __fenv_setmxcsr(&mxcsr
);
666 /* decode the operation */
667 __fex_get_op(sip
, uap
, &info
);
669 /* if a custom mode handler is installed, invoke it */
670 if (mode
== FEX_CUSTOM
) {
671 /* if we got here from feraiseexcept, pass dummy info */
672 if (addr
>= (unsigned long)feraiseexcept
&&
673 addr
< (unsigned long)fetestexcept
) {
675 info
.op1
.type
= info
.op2
.type
= info
.res
.type
=
679 handler(1 << (int)e
, &info
);
681 /* restore modes in case the user's handler changed them */
682 __fenv_setcwsw(&cwsw
);
684 __fenv_setmxcsr(&mxcsr
);
687 /* stuff the result */
688 __fex_st_result(sip
, uap
, &info
);
689 accrued
|= info
.flags
;
693 i
= __fex_te_needed(thr_handlers
, accrued
);
696 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.sw
&= ~0x3d;
697 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.sw
|= (accrued
& ~i
);
698 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.cw
|= 0x3d;
699 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.cw
&= ~i
;
701 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[1] &= ~0x3d;
702 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[1] |=
704 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[0] |= 0x3d;
705 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.state
[0] &= ~i
;
708 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
&= ~0x3d;
709 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
|=
710 0x1e80 | (accrued
& ~i
);
711 uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.mxcsr
&=
717 /* revert to the saved handler (if any) */
718 mutex_lock(&hdlr_lock
);
720 mutex_unlock(&hdlr_lock
);
721 switch ((unsigned long)act
.sa_handler
) {
722 case (unsigned long)SIG_DFL
:
723 /* simulate trap with no handler installed */
724 sigaction(SIGFPE
, &act
, NULL
);
725 kill(getpid(), SIGFPE
);
727 case (unsigned long)SIG_IGN
:
730 act
.sa_handler(sig
, &osip
, uap
);
735 #error Unknown architecture
739 * Return a pointer to the thread-specific handler data, and
740 * initialize it if necessary
742 struct fex_handler_data
*
743 __fex_get_thr_handlers()
745 struct fex_handler_data
*ptr
;
750 if (!handlers_initialized
) {
751 /* initialize to FEX_NOHANDLER if trap is enabled,
752 FEX_NONSTOP if trap is disabled */
754 te
= (int)__fenv_get_te(fsr
);
755 for (i
= 0; i
< FEX_NUM_EXC
; i
++)
756 main_handlers
[i
].__mode
=
757 ((te
& te_bit
[i
])? FEX_NOHANDLER
: FEX_NONSTOP
);
758 handlers_initialized
= 1;
760 return main_handlers
;
764 mutex_lock(&handlers_key_lock
);
765 if (thr_getspecific(handlers_key
, (void **)&ptr
) != 0 &&
766 thr_keycreate(&handlers_key
, free
) != 0) {
767 mutex_unlock(&handlers_key_lock
);
770 mutex_unlock(&handlers_key_lock
);
772 if ((ptr
= (struct fex_handler_data
*)
773 malloc(sizeof(fex_handler_t
))) == NULL
) {
776 if (thr_setspecific(handlers_key
, (void *)ptr
) != 0) {
780 /* initialize to FEX_NOHANDLER if trap is enabled,
781 FEX_NONSTOP if trap is disabled */
783 te
= (int)__fenv_get_te(fsr
);
784 for (i
= 0; i
< FEX_NUM_EXC
; i
++)
785 ptr
[i
].__mode
= ((te
& te_bit
[i
])? FEX_NOHANDLER
: FEX_NONSTOP
);
792 * Update the trap enable bits according to the selected modes
797 struct fex_handler_data
*thr_handlers
;
798 struct sigaction act
, tmpact
;
803 /* determine which traps are needed */
804 thr_handlers
= __fex_get_thr_handlers();
806 te
= __fex_te_needed(thr_handlers
, fsr
);
808 /* install __fex_hdlr as necessary */
809 if (!hdlr_installed
&& te
) {
810 act
.sa_handler
= __fex_hdlr
;
811 sigemptyset(&act
.sa_mask
);
812 act
.sa_flags
= SA_SIGINFO
;
813 sigaction(SIGFPE
, &act
, &tmpact
);
814 if (tmpact
.sa_handler
!= __fex_hdlr
)
816 mutex_lock(&hdlr_lock
);
818 mutex_unlock(&hdlr_lock
);
823 /* set the new trap enable bits (only if SIGFPE is not blocked) */
824 if (sigprocmask(0, NULL
, &blocked
) == 0 &&
825 !sigismember(&blocked
, SIGFPE
)) {
826 __fenv_set_te(fsr
, te
);
830 /* synchronize with libmtsk */
831 __mt_fex_sync
= __fex_sync_with_libmtsk
;
833 /* synchronize with other projects */
834 __libm_mt_fex_sync
= __fex_sync_with_threads
;