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.
39 #if defined(__SUNPRO_C)
43 #include "fex_handler.h"
44 #include "fenv_inlines.h"
46 #if defined(__sparc) && !defined(__sparcv9)
47 #include <sys/procfs.h>
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);
55 /* external globals */
56 void (*__mt_fex_sync
)() = NULL
; /* for synchronization with libmtsk */
57 #pragma weak __mt_fex_sync
59 void (*__libm_mt_fex_sync
)() = NULL
; /* new, improved version of above */
60 #pragma weak __libm_mt_fex_sync
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
;
68 static struct sigaction oact
= { 0, SIG_DFL
};
69 static mutex_t hdlr_lock
= DEFAULTMUTEX
;
70 static int hdlr_installed
= 0;
72 /* private const data */
73 static const int te_bit
[FEX_NUM_EXC
] = {
75 1 << fp_trap_division
,
76 1 << fp_trap_underflow
,
77 1 << fp_trap_overflow
,
89 * Return the traps to be enabled given the current handling modes
93 __fex_te_needed(struct fex_handler_data
*thr_handlers
, unsigned long fsr
)
97 /* set traps for handling modes */
99 for (i
= 0; i
< FEX_NUM_EXC
; i
++)
100 if (thr_handlers
[i
].__mode
!= FEX_NONSTOP
)
103 /* add traps for retrospective diagnostics */
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
);
122 * The following function synchronizes with libmtsk (SPARC only, for now)
125 __fex_sync_with_libmtsk(int begin
, int master
)
127 static fenv_t master_env
;
128 static int env_initialized
= 0;
129 static mutex_t env_lock
= DEFAULTMUTEX
;
132 mutex_lock(&env_lock
);
134 (void) fegetenv(&master_env
);
137 else if (env_initialized
)
138 (void) fesetenv(&master_env
);
139 mutex_unlock(&env_lock
);
141 else if (master
&& fex_get_log())
146 * The following function may be used for synchronization with any
147 * internal project that manages multiple threads
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
156 struct __libm_mt_fex_sync_data
{
163 __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action
,
164 struct __libm_mt_fex_sync_data
*thr_env
)
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
);
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
);
181 case __libm_mt_fex_finish_master
:
190 case __libm_mt_fex_finish_slave
:
192 /* clear traps, making all accrued flags visible in status word */
196 __fenv_set_te(fsr
, 0);
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.)
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 */
222 * If a handling mode is in effect, apply it; otherwise invoke the
226 __fex_hdlr(int sig
, siginfo_t
*sip
, ucontext_t
*uap
)
228 struct fex_handler_data
*thr_handlers
;
229 struct sigaction act
;
230 void (*handler
)(), (*siamp
)();
232 enum fex_exception e
;
234 unsigned long fsr
, tmpfsr
, addr
;
237 /* determine which exception occurred */
238 switch (sip
->si_code
) {
252 if ((int)(e
= __fex_get_invalid_type(sip
, uap
)) < 0)
256 /* not an IEEE exception */
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
;
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
);
273 /* handle the exception based on the mode */
274 if (mode
== FEX_NOHANDLER
)
276 else if (mode
== FEX_ABORT
)
278 else if (mode
== FEX_SIGNAL
) {
279 handler(sig
, sip
, uap
);
283 /* custom or nonstop mode; disable traps and clear flags */
285 __fenv_set_te(fsr
, 0);
286 __fenv_set_ex(fsr
, 0);
288 /* if interval mode was set, clear it, then substitute the
289 interval rounding direction and clear ns mode in the fsr */
291 gsr
= uap
->uc_mcontext
.asrs
[3];
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
);
298 gsr
= (gsr
>> 25) & 7;
300 siamp
= (void (*)()) siam
[0];
303 fsr
= (fsr
& ~0xc0400000ul
) | ((gsr
& 3) << 30);
307 /* decode the operation */
308 __fex_get_op(sip
, uap
, &info
);
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
) {
317 info
.op1
.type
= info
.op2
.type
= info
.res
.type
=
321 /* restore interval mode if it was set, and put the original
322 rounding direction and ns mode back in the fsr */
324 __fenv_setfsr(&tmpfsr
);
325 siamp
= (void (*)()) siam
[1 + (gsr
& 3)];
329 handler(1 << (int)e
, &info
);
331 /* restore modes in case the user's handler changed them */
333 siamp
= (void (*)()) siam
[0];
339 /* stuff the result */
340 __fex_st_result(sip
, uap
, &info
);
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
;
351 /* revert to the saved handler (if any) */
352 mutex_lock(&hdlr_lock
);
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
);
362 case (unsigned long)SIG_IGN
:
366 act
.sa_handler(sig
, sip
, uap
);
373 #define test_sse_hw 1
376 #define test_sse_hw _sse_hw
384 * If a handling mode is in effect, apply it; otherwise invoke the
388 __fex_hdlr(int sig
, siginfo_t
*sip
, ucontext_t
*uap
)
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];
398 siginfo_t osip
= *sip
;
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
);
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
);
415 if ((int)inst
.op
& SIMD
) {
416 __fex_get_simd_op(uap
, &inst
, simd_e
, simd_info
);
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
.
423 e
= (enum fex_exception
)-1;
425 for (i
= 0; i
< 4; i
++) {
426 if ((int)simd_e
[i
] < 0)
430 simd_mode
[i
] = FEX_NOHANDLER
;
431 simd_handler
[i
] = oact
.sa_handler
;
433 thr_handlers
[(int)e
].__mode
!=
436 thr_handlers
[(int)e
].__mode
;
438 thr_handlers
[(int)e
].__handler
;
440 accrued
&= ~te_bit
[(int)e
];
441 switch (simd_mode
[i
]) {
446 if (mode
!= FEX_ABORT
)
448 handler
= simd_handler
[i
];
451 if (mode
!= FEX_ABORT
&& mode
!=
453 mode
= FEX_NOHANDLER
;
457 if (e
== (enum fex_exception
)-1) {
458 __fenv_setcwsw(&oldcwsw
);
459 __fenv_setmxcsr(&oldmxcsr
);
462 accrued
|= uap
->uc_mcontext
.fpregs
.fp_reg_set
.
464 ap
= __fex_accrued();
468 for (i
= 0; i
< 4; i
++) {
469 if ((int)simd_e
[i
] < 0)
472 __fex_mklog(uap
, (char *)addr
, accrued
,
473 simd_e
[i
], simd_mode
[i
],
474 (void *)simd_handler
[i
]);
477 if (mode
== FEX_NOHANDLER
) {
478 __fenv_setcwsw(&oldcwsw
);
479 __fenv_setmxcsr(&oldmxcsr
);
481 } else if (mode
== FEX_ABORT
) {
483 } else if (mode
== FEX_SIGNAL
) {
484 __fenv_setcwsw(&oldcwsw
);
485 __fenv_setmxcsr(&oldmxcsr
);
486 handler(sig
, &osip
, uap
);
491 for (i
= 0; i
< 4; i
++) {
492 if ((int)simd_e
[i
] < 0)
495 if (simd_mode
[i
] == FEX_CUSTOM
) {
496 handler(1 << (int)simd_e
[i
],
498 __fenv_setcwsw(&cwsw
);
499 __fenv_setmxcsr(&mxcsr
);
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)
508 accrued
|= simd_info
[i
].flags
;
511 if ((int)inst
.op
& INTREG
) {
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;
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;
526 e
= __fex_get_sse_op(uap
, &inst
, &info
);
528 __fenv_setcwsw(&oldcwsw
);
529 __fenv_setmxcsr(&oldmxcsr
);
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
!=
538 mode
= thr_handlers
[(int)e
].__mode
;
539 handler
= thr_handlers
[(int)e
].__handler
;
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
.
547 ap
= __fex_accrued();
550 __fex_mklog(uap
, (char *)addr
, accrued
, e
, mode
,
553 if (mode
== FEX_NOHANDLER
) {
554 __fenv_setcwsw(&oldcwsw
);
555 __fenv_setmxcsr(&oldmxcsr
);
557 } else if (mode
== FEX_ABORT
) {
559 } else if (mode
== FEX_SIGNAL
) {
560 __fenv_setcwsw(&oldcwsw
);
561 __fenv_setmxcsr(&oldmxcsr
);
562 handler(sig
, &osip
, uap
);
564 } else if (mode
== FEX_CUSTOM
) {
566 if (addr
>= (unsigned long)feraiseexcept
&&
567 addr
< (unsigned long)fetestexcept
) {
569 info
.op1
.type
= info
.op2
.type
=
570 info
.res
.type
= fex_nodata
;
572 handler(1 << (int)e
, &info
);
573 __fenv_setcwsw(&cwsw
);
574 __fenv_setmxcsr(&mxcsr
);
577 __fex_st_sse_result(uap
, &inst
, e
, &info
);
578 accrued
|= info
.flags
;
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.)
588 if (inst
.op
== cvtss2si
|| inst
.op
== cvttss2si
||
589 inst
.op
== cvtsd2si
|| inst
.op
== cvttsd2si
)
594 /* advance the pc past the SSE instruction */
595 uap
->uc_mcontext
.gregs
[REG_PC
] += len
;
599 /* determine which exception occurred */
600 __fex_get_x86_exc(sip
, uap
);
601 switch (sip
->si_code
) {
615 if ((int)(e
= __fex_get_invalid_type(sip
, uap
)) < 0)
619 /* not an IEEE exception */
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
;
632 /* make an entry in the log of retro. diag. if need be */
634 addr
= (unsigned long)uap
->uc_mcontext
.fpregs
.fp_reg_set
.
637 addr
= (unsigned long)uap
->uc_mcontext
.fpregs
.fp_reg_set
.
638 fpchip_state
.state
[3];
640 accrued
= uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.status
&
643 accrued
|= uap
->uc_mcontext
.fpregs
.fp_reg_set
.fpchip_state
.
645 ap
= __fex_accrued();
648 __fex_mklog(uap
, (char *)addr
, accrued
, e
, mode
, (void *)handler
);
650 /* handle the exception based on the mode */
651 if (mode
== FEX_NOHANDLER
)
653 else if (mode
== FEX_ABORT
)
655 else if (mode
== FEX_SIGNAL
) {
656 handler(sig
, &osip
, uap
);
660 /* disable all traps and clear flags */
661 __fenv_getcwsw(&cwsw
);
662 cwsw
= (cwsw
& ~0x3f) | 0x003f0000;
663 __fenv_setcwsw(&cwsw
);
665 __fenv_getmxcsr(&mxcsr
);
666 mxcsr
= (mxcsr
& ~0x3f) | 0x1f80;
667 __fenv_setmxcsr(&mxcsr
);
671 /* decode the operation */
672 __fex_get_op(sip
, uap
, &info
);
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
) {
680 info
.op1
.type
= info
.op2
.type
= info
.res
.type
=
684 handler(1 << (int)e
, &info
);
686 /* restore modes in case the user's handler changed them */
687 __fenv_setcwsw(&cwsw
);
689 __fenv_setmxcsr(&mxcsr
);
692 /* stuff the result */
693 __fex_st_result(sip
, uap
, &info
);
694 accrued
|= info
.flags
;
698 i
= __fex_te_needed(thr_handlers
, accrued
);
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
;
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] |=
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
;
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
&=
722 /* revert to the saved handler (if any) */
723 mutex_lock(&hdlr_lock
);
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
);
733 case (unsigned long)SIG_IGN
:
737 act
.sa_handler(sig
, &osip
, uap
);
742 #error Unknown architecture
746 * Return a pointer to the thread-specific handler data, and
747 * initialize it if necessary
749 struct fex_handler_data
*
750 __fex_get_thr_handlers()
752 struct fex_handler_data
*ptr
;
757 if (!handlers_initialized
) {
758 /* initialize to FEX_NOHANDLER if trap is enabled,
759 FEX_NONSTOP if trap is disabled */
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;
767 return main_handlers
;
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
);
777 mutex_unlock(&handlers_key_lock
);
779 if ((ptr
= (struct fex_handler_data
*)
780 malloc(sizeof(fex_handler_t
))) == NULL
) {
783 if (thr_setspecific(handlers_key
, (void *)ptr
) != 0) {
787 /* initialize to FEX_NOHANDLER if trap is enabled,
788 FEX_NONSTOP if trap is disabled */
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
);
799 * Update the trap enable bits according to the selected modes
804 struct fex_handler_data
*thr_handlers
;
805 struct sigaction act
, tmpact
;
810 /* determine which traps are needed */
811 thr_handlers
= __fex_get_thr_handlers();
813 te
= __fex_te_needed(thr_handlers
, fsr
);
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
)
823 mutex_lock(&hdlr_lock
);
825 mutex_unlock(&hdlr_lock
);
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
);
837 /* synchronize with libmtsk */
838 __mt_fex_sync
= __fex_sync_with_libmtsk
;
840 /* synchronize with other projects */
841 __libm_mt_fex_sync
= __fex_sync_with_threads
;