125c28e83SPiotr Jasiukajtis /*
225c28e83SPiotr Jasiukajtis  * CDDL HEADER START
325c28e83SPiotr Jasiukajtis  *
425c28e83SPiotr Jasiukajtis  * The contents of this file are subject to the terms of the
525c28e83SPiotr Jasiukajtis  * Common Development and Distribution License (the "License").
625c28e83SPiotr Jasiukajtis  * You may not use this file except in compliance with the License.
725c28e83SPiotr Jasiukajtis  *
825c28e83SPiotr Jasiukajtis  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925c28e83SPiotr Jasiukajtis  * or http://www.opensolaris.org/os/licensing.
1025c28e83SPiotr Jasiukajtis  * See the License for the specific language governing permissions
1125c28e83SPiotr Jasiukajtis  * and limitations under the License.
1225c28e83SPiotr Jasiukajtis  *
1325c28e83SPiotr Jasiukajtis  * When distributing Covered Code, include this CDDL HEADER in each
1425c28e83SPiotr Jasiukajtis  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525c28e83SPiotr Jasiukajtis  * If applicable, add the following below this CDDL HEADER, with the
1625c28e83SPiotr Jasiukajtis  * fields enclosed by brackets "[]" replaced with your own identifying
1725c28e83SPiotr Jasiukajtis  * information: Portions Copyright [yyyy] [name of copyright owner]
1825c28e83SPiotr Jasiukajtis  *
1925c28e83SPiotr Jasiukajtis  * CDDL HEADER END
2025c28e83SPiotr Jasiukajtis  */
2125c28e83SPiotr Jasiukajtis 
2225c28e83SPiotr Jasiukajtis /*
2325c28e83SPiotr Jasiukajtis  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
2425c28e83SPiotr Jasiukajtis  */
2525c28e83SPiotr Jasiukajtis /*
2625c28e83SPiotr Jasiukajtis  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2725c28e83SPiotr Jasiukajtis  * Use is subject to license terms.
2825c28e83SPiotr Jasiukajtis  */
2925c28e83SPiotr Jasiukajtis 
3025c28e83SPiotr Jasiukajtis #undef lint
3125c28e83SPiotr Jasiukajtis #include <signal.h>
3225c28e83SPiotr Jasiukajtis #include <siginfo.h>
3325c28e83SPiotr Jasiukajtis #include <ucontext.h>
3425c28e83SPiotr Jasiukajtis #include <stdio.h>
3525c28e83SPiotr Jasiukajtis #include <stdlib.h>
3625c28e83SPiotr Jasiukajtis #include <unistd.h>
3725c28e83SPiotr Jasiukajtis #include <thread.h>
3825c28e83SPiotr Jasiukajtis #include <math.h>
3925c28e83SPiotr Jasiukajtis #if defined(__SUNPRO_C)
4025c28e83SPiotr Jasiukajtis #include <sunmath.h>
4125c28e83SPiotr Jasiukajtis #endif
4225c28e83SPiotr Jasiukajtis #include <fenv.h>
4325c28e83SPiotr Jasiukajtis #include "fex_handler.h"
4425c28e83SPiotr Jasiukajtis #include "fenv_inlines.h"
4525c28e83SPiotr Jasiukajtis 
4625c28e83SPiotr Jasiukajtis #if defined(__sparc) && !defined(__sparcv9)
4725c28e83SPiotr Jasiukajtis #include <sys/procfs.h>
4825c28e83SPiotr Jasiukajtis #endif
4925c28e83SPiotr Jasiukajtis 
5025c28e83SPiotr Jasiukajtis /* 2.x signal.h doesn't declare sigemptyset or sigismember
5125c28e83SPiotr Jasiukajtis    if they're #defined (see sys/signal.h) */
5225c28e83SPiotr Jasiukajtis extern int sigemptyset(sigset_t *);
5325c28e83SPiotr Jasiukajtis extern int sigismember(const sigset_t *, int);
5425c28e83SPiotr Jasiukajtis 
5525c28e83SPiotr Jasiukajtis /* external globals */
5625c28e83SPiotr Jasiukajtis void (*__mt_fex_sync)() = NULL; /* for synchronization with libmtsk */
5725c28e83SPiotr Jasiukajtis #pragma weak __mt_fex_sync
5825c28e83SPiotr Jasiukajtis 
5925c28e83SPiotr Jasiukajtis void (*__libm_mt_fex_sync)() = NULL; /* new, improved version of above */
6025c28e83SPiotr Jasiukajtis #pragma weak __libm_mt_fex_sync
6125c28e83SPiotr Jasiukajtis 
6225c28e83SPiotr Jasiukajtis /* private variables */
6325c28e83SPiotr Jasiukajtis static fex_handler_t main_handlers;
6425c28e83SPiotr Jasiukajtis static int handlers_initialized = 0;
6525c28e83SPiotr Jasiukajtis static thread_key_t handlers_key;
6625c28e83SPiotr Jasiukajtis static mutex_t handlers_key_lock = DEFAULTMUTEX;
6725c28e83SPiotr Jasiukajtis 
6825c28e83SPiotr Jasiukajtis static struct sigaction oact = { 0, SIG_DFL };
6925c28e83SPiotr Jasiukajtis static mutex_t hdlr_lock = DEFAULTMUTEX;
7025c28e83SPiotr Jasiukajtis static int hdlr_installed = 0;
7125c28e83SPiotr Jasiukajtis 
7225c28e83SPiotr Jasiukajtis /* private const data */
7325c28e83SPiotr Jasiukajtis static const int te_bit[FEX_NUM_EXC] = {
7425c28e83SPiotr Jasiukajtis 	1 << fp_trap_inexact,
7525c28e83SPiotr Jasiukajtis 	1 << fp_trap_division,
7625c28e83SPiotr Jasiukajtis 	1 << fp_trap_underflow,
7725c28e83SPiotr Jasiukajtis 	1 << fp_trap_overflow,
7825c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
7925c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8025c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8125c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8225c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8325c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8425c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid,
8525c28e83SPiotr Jasiukajtis 	1 << fp_trap_invalid
8625c28e83SPiotr Jasiukajtis };
8725c28e83SPiotr Jasiukajtis 
8825c28e83SPiotr Jasiukajtis /*
8925c28e83SPiotr Jasiukajtis *  Return the traps to be enabled given the current handling modes
9025c28e83SPiotr Jasiukajtis *  and flags
9125c28e83SPiotr Jasiukajtis */
9225c28e83SPiotr Jasiukajtis static int
__fex_te_needed(struct fex_handler_data * thr_handlers,unsigned long fsr)9325c28e83SPiotr Jasiukajtis __fex_te_needed(struct fex_handler_data *thr_handlers, unsigned long fsr)
9425c28e83SPiotr Jasiukajtis {
9525c28e83SPiotr Jasiukajtis 	int		i, ex, te;
9625c28e83SPiotr Jasiukajtis 
9725c28e83SPiotr Jasiukajtis 	/* set traps for handling modes */
9825c28e83SPiotr Jasiukajtis 	te = 0;
9925c28e83SPiotr Jasiukajtis 	for (i = 0; i < FEX_NUM_EXC; i++)
10025c28e83SPiotr Jasiukajtis 		if (thr_handlers[i].__mode != FEX_NONSTOP)
10125c28e83SPiotr Jasiukajtis 			te |= te_bit[i];
10225c28e83SPiotr Jasiukajtis 
10325c28e83SPiotr Jasiukajtis 	/* add traps for retrospective diagnostics */
10425c28e83SPiotr Jasiukajtis 	if (fex_get_log()) {
10525c28e83SPiotr Jasiukajtis 		ex = (int)__fenv_get_ex(fsr);
10625c28e83SPiotr Jasiukajtis 		if (!(ex & FE_INEXACT))
10725c28e83SPiotr Jasiukajtis 			te |= (1 << fp_trap_inexact);
10825c28e83SPiotr Jasiukajtis 		if (!(ex & FE_UNDERFLOW))
10925c28e83SPiotr Jasiukajtis 			te |= (1 << fp_trap_underflow);
11025c28e83SPiotr Jasiukajtis 		if (!(ex & FE_OVERFLOW))
11125c28e83SPiotr Jasiukajtis 			te |= (1 << fp_trap_overflow);
11225c28e83SPiotr Jasiukajtis 		if (!(ex & FE_DIVBYZERO))
11325c28e83SPiotr Jasiukajtis 			te |= (1 << fp_trap_division);
11425c28e83SPiotr Jasiukajtis 		if (!(ex & FE_INVALID))
11525c28e83SPiotr Jasiukajtis 			te |= (1 << fp_trap_invalid);
11625c28e83SPiotr Jasiukajtis 	}
11725c28e83SPiotr Jasiukajtis 
11825c28e83SPiotr Jasiukajtis 	return te;
11925c28e83SPiotr Jasiukajtis }
12025c28e83SPiotr Jasiukajtis 
12125c28e83SPiotr Jasiukajtis /*
12225c28e83SPiotr Jasiukajtis *  The following function synchronizes with libmtsk (SPARC only, for now)
12325c28e83SPiotr Jasiukajtis */
12425c28e83SPiotr Jasiukajtis static void
__fex_sync_with_libmtsk(int begin,int master)12525c28e83SPiotr Jasiukajtis __fex_sync_with_libmtsk(int begin, int master)
12625c28e83SPiotr Jasiukajtis {
12725c28e83SPiotr Jasiukajtis 	static fenv_t master_env;
12825c28e83SPiotr Jasiukajtis 	static int env_initialized = 0;
12925c28e83SPiotr Jasiukajtis 	static mutex_t env_lock = DEFAULTMUTEX;
13025c28e83SPiotr Jasiukajtis 
13125c28e83SPiotr Jasiukajtis 	if (begin) {
13225c28e83SPiotr Jasiukajtis 		mutex_lock(&env_lock);
13325c28e83SPiotr Jasiukajtis 		if (master) {
13425c28e83SPiotr Jasiukajtis 			(void) fegetenv(&master_env);
13525c28e83SPiotr Jasiukajtis 			env_initialized = 1;
13625c28e83SPiotr Jasiukajtis 		}
13725c28e83SPiotr Jasiukajtis 		else if (env_initialized)
13825c28e83SPiotr Jasiukajtis 			(void) fesetenv(&master_env);
13925c28e83SPiotr Jasiukajtis 		mutex_unlock(&env_lock);
14025c28e83SPiotr Jasiukajtis 	}
14125c28e83SPiotr Jasiukajtis 	else if (master && fex_get_log())
14225c28e83SPiotr Jasiukajtis 		__fex_update_te();
14325c28e83SPiotr Jasiukajtis }
14425c28e83SPiotr Jasiukajtis 
14525c28e83SPiotr Jasiukajtis /*
14625c28e83SPiotr Jasiukajtis *  The following function may be used for synchronization with any
14725c28e83SPiotr Jasiukajtis *  internal project that manages multiple threads
14825c28e83SPiotr Jasiukajtis */
14925c28e83SPiotr Jasiukajtis enum __libm_mt_fex_sync_actions {
15025c28e83SPiotr Jasiukajtis 	__libm_mt_fex_start_master = 0,
15125c28e83SPiotr Jasiukajtis 	__libm_mt_fex_start_slave,
15225c28e83SPiotr Jasiukajtis 	__libm_mt_fex_finish_master,
15325c28e83SPiotr Jasiukajtis 	__libm_mt_fex_finish_slave
15425c28e83SPiotr Jasiukajtis };
15525c28e83SPiotr Jasiukajtis 
15625c28e83SPiotr Jasiukajtis struct __libm_mt_fex_sync_data {
15725c28e83SPiotr Jasiukajtis 	fenv_t	master_env;
15825c28e83SPiotr Jasiukajtis 	int		initialized;
15925c28e83SPiotr Jasiukajtis 	mutex_t	lock;
16025c28e83SPiotr Jasiukajtis };
16125c28e83SPiotr Jasiukajtis 
16225c28e83SPiotr Jasiukajtis static void
__fex_sync_with_threads(enum __libm_mt_fex_sync_actions action,struct __libm_mt_fex_sync_data * thr_env)16325c28e83SPiotr Jasiukajtis __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action,
16425c28e83SPiotr Jasiukajtis 	struct __libm_mt_fex_sync_data *thr_env)
16525c28e83SPiotr Jasiukajtis {
16625c28e83SPiotr Jasiukajtis 	switch (action) {
16725c28e83SPiotr Jasiukajtis 	case __libm_mt_fex_start_master:
16825c28e83SPiotr Jasiukajtis 		mutex_lock(&thr_env->lock);
16925c28e83SPiotr Jasiukajtis 		(void) fegetenv(&thr_env->master_env);
17025c28e83SPiotr Jasiukajtis 		thr_env->initialized = 1;
17125c28e83SPiotr Jasiukajtis 		mutex_unlock(&thr_env->lock);
17225c28e83SPiotr Jasiukajtis 		break;
17325c28e83SPiotr Jasiukajtis 
17425c28e83SPiotr Jasiukajtis 	case __libm_mt_fex_start_slave:
17525c28e83SPiotr Jasiukajtis 		mutex_lock(&thr_env->lock);
17625c28e83SPiotr Jasiukajtis 		if (thr_env->initialized)
17725c28e83SPiotr Jasiukajtis 			(void) fesetenv(&thr_env->master_env);
17825c28e83SPiotr Jasiukajtis 		mutex_unlock(&thr_env->lock);
17925c28e83SPiotr Jasiukajtis 		break;
18025c28e83SPiotr Jasiukajtis 
18125c28e83SPiotr Jasiukajtis 	case __libm_mt_fex_finish_master:
18225c28e83SPiotr Jasiukajtis #if defined(__x86)
18325c28e83SPiotr Jasiukajtis 		__fex_update_te();
18425c28e83SPiotr Jasiukajtis #else
18525c28e83SPiotr Jasiukajtis 		if (fex_get_log())
18625c28e83SPiotr Jasiukajtis 			__fex_update_te();
18725c28e83SPiotr Jasiukajtis #endif
18825c28e83SPiotr Jasiukajtis 		break;
18925c28e83SPiotr Jasiukajtis 
19025c28e83SPiotr Jasiukajtis 	case __libm_mt_fex_finish_slave:
19125c28e83SPiotr Jasiukajtis #if defined(__x86)
19225c28e83SPiotr Jasiukajtis 		/* clear traps, making all accrued flags visible in status word */
19325c28e83SPiotr Jasiukajtis 		{
19425c28e83SPiotr Jasiukajtis 			unsigned long   fsr;
19525c28e83SPiotr Jasiukajtis 			__fenv_getfsr(&fsr);
19625c28e83SPiotr Jasiukajtis 			__fenv_set_te(fsr, 0);
19725c28e83SPiotr Jasiukajtis 			__fenv_setfsr(&fsr);
19825c28e83SPiotr Jasiukajtis 		}
19925c28e83SPiotr Jasiukajtis #endif
20025c28e83SPiotr Jasiukajtis 		break;
20125c28e83SPiotr Jasiukajtis 	}
20225c28e83SPiotr Jasiukajtis }
20325c28e83SPiotr Jasiukajtis 
20425c28e83SPiotr Jasiukajtis #if defined(__sparc)
20525c28e83SPiotr Jasiukajtis 
20625c28e83SPiotr Jasiukajtis /*
20725c28e83SPiotr Jasiukajtis *  Code for setting or clearing interval mode on US-III and above.
20825c28e83SPiotr Jasiukajtis *  This is embedded as data so we don't have to mark the library
20925c28e83SPiotr Jasiukajtis *  as a v8plusb/v9b object.  (I could have just used one entry and
21025c28e83SPiotr Jasiukajtis *  modified the second word to set the bits I want, but that would
21125c28e83SPiotr Jasiukajtis *  have required another mutex.)
21225c28e83SPiotr Jasiukajtis */
21325c28e83SPiotr Jasiukajtis static const unsigned int siam[][2] = {
21425c28e83SPiotr Jasiukajtis 	{ 0x81c3e008, 0x81b01020 }, /* retl, siam 0 */
21525c28e83SPiotr Jasiukajtis 	{ 0x81c3e008, 0x81b01024 }, /* retl, siam 4 */
21625c28e83SPiotr Jasiukajtis 	{ 0x81c3e008, 0x81b01025 }, /* retl, siam 5 */
21725c28e83SPiotr Jasiukajtis 	{ 0x81c3e008, 0x81b01026 }, /* retl, siam 6 */
21825c28e83SPiotr Jasiukajtis 	{ 0x81c3e008, 0x81b01027 }  /* retl, siam 7 */
21925c28e83SPiotr Jasiukajtis };
22025c28e83SPiotr Jasiukajtis 
22125c28e83SPiotr Jasiukajtis /*
22225c28e83SPiotr Jasiukajtis *  If a handling mode is in effect, apply it; otherwise invoke the
22325c28e83SPiotr Jasiukajtis *  saved handler
22425c28e83SPiotr Jasiukajtis */
22525c28e83SPiotr Jasiukajtis static void
__fex_hdlr(int sig,siginfo_t * sip,void * arg)226*30699046SRichard Lowe __fex_hdlr(int sig, siginfo_t *sip, void *arg)
22725c28e83SPiotr Jasiukajtis {
228*30699046SRichard Lowe 	ucontext_t		*uap = arg;
22925c28e83SPiotr Jasiukajtis 	struct fex_handler_data	*thr_handlers;
23025c28e83SPiotr Jasiukajtis 	struct sigaction	act;
23125c28e83SPiotr Jasiukajtis 	void			(*handler)(), (*siamp)();
23225c28e83SPiotr Jasiukajtis 	int			mode, i;
23325c28e83SPiotr Jasiukajtis 	enum fex_exception	e;
23425c28e83SPiotr Jasiukajtis 	fex_info_t		info;
23525c28e83SPiotr Jasiukajtis 	unsigned long		fsr, tmpfsr, addr;
23625c28e83SPiotr Jasiukajtis 	unsigned int		gsr;
23725c28e83SPiotr Jasiukajtis 
23825c28e83SPiotr Jasiukajtis 	/* determine which exception occurred */
23925c28e83SPiotr Jasiukajtis 	switch (sip->si_code) {
24025c28e83SPiotr Jasiukajtis 	case FPE_FLTDIV:
24125c28e83SPiotr Jasiukajtis 		e = fex_division;
24225c28e83SPiotr Jasiukajtis 		break;
24325c28e83SPiotr Jasiukajtis 	case FPE_FLTOVF:
24425c28e83SPiotr Jasiukajtis 		e = fex_overflow;
24525c28e83SPiotr Jasiukajtis 		break;
24625c28e83SPiotr Jasiukajtis 	case FPE_FLTUND:
24725c28e83SPiotr Jasiukajtis 		e = fex_underflow;
24825c28e83SPiotr Jasiukajtis 		break;
24925c28e83SPiotr Jasiukajtis 	case FPE_FLTRES:
25025c28e83SPiotr Jasiukajtis 		e = fex_inexact;
25125c28e83SPiotr Jasiukajtis 		break;
25225c28e83SPiotr Jasiukajtis 	case FPE_FLTINV:
25325c28e83SPiotr Jasiukajtis 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
25425c28e83SPiotr Jasiukajtis 			goto not_ieee;
25525c28e83SPiotr Jasiukajtis 		break;
25625c28e83SPiotr Jasiukajtis 	default:
25725c28e83SPiotr Jasiukajtis 		/* not an IEEE exception */
25825c28e83SPiotr Jasiukajtis 		goto not_ieee;
25925c28e83SPiotr Jasiukajtis 	}
26025c28e83SPiotr Jasiukajtis 
26125c28e83SPiotr Jasiukajtis 	/* get the handling mode */
26225c28e83SPiotr Jasiukajtis 	mode = FEX_NOHANDLER;
26325c28e83SPiotr Jasiukajtis 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
26425c28e83SPiotr Jasiukajtis 	thr_handlers = __fex_get_thr_handlers();
26525c28e83SPiotr Jasiukajtis 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
26625c28e83SPiotr Jasiukajtis 		mode = thr_handlers[(int)e].__mode;
26725c28e83SPiotr Jasiukajtis 		handler = thr_handlers[(int)e].__handler;
26825c28e83SPiotr Jasiukajtis 	}
26925c28e83SPiotr Jasiukajtis 
27025c28e83SPiotr Jasiukajtis 	/* make an entry in the log of retro. diag. if need be */
27125c28e83SPiotr Jasiukajtis 	i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
27225c28e83SPiotr Jasiukajtis 	__fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
27325c28e83SPiotr Jasiukajtis 
27425c28e83SPiotr Jasiukajtis 	/* handle the exception based on the mode */
27525c28e83SPiotr Jasiukajtis 	if (mode == FEX_NOHANDLER)
27625c28e83SPiotr Jasiukajtis 		goto not_ieee;
27725c28e83SPiotr Jasiukajtis 	else if (mode == FEX_ABORT)
27825c28e83SPiotr Jasiukajtis 		abort();
27925c28e83SPiotr Jasiukajtis 	else if (mode == FEX_SIGNAL) {
28025c28e83SPiotr Jasiukajtis 		handler(sig, sip, uap);
28125c28e83SPiotr Jasiukajtis 		return;
28225c28e83SPiotr Jasiukajtis 	}
28325c28e83SPiotr Jasiukajtis 
28425c28e83SPiotr Jasiukajtis 	/* custom or nonstop mode; disable traps and clear flags */
28525c28e83SPiotr Jasiukajtis 	__fenv_getfsr(&fsr);
28625c28e83SPiotr Jasiukajtis 	__fenv_set_te(fsr, 0);
28725c28e83SPiotr Jasiukajtis 	__fenv_set_ex(fsr, 0);
28825c28e83SPiotr Jasiukajtis 
28925c28e83SPiotr Jasiukajtis 	/* if interval mode was set, clear it, then substitute the
29025c28e83SPiotr Jasiukajtis 	   interval rounding direction and clear ns mode in the fsr */
29125c28e83SPiotr Jasiukajtis #ifdef __sparcv9
29225c28e83SPiotr Jasiukajtis 	gsr = uap->uc_mcontext.asrs[3];
29325c28e83SPiotr Jasiukajtis #else
29425c28e83SPiotr Jasiukajtis 	gsr = 0;
29525c28e83SPiotr Jasiukajtis 	if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
29625c28e83SPiotr Jasiukajtis 		gsr = (*(unsigned long long*)((prxregset_t*)uap->uc_mcontext.
29725c28e83SPiotr Jasiukajtis 		    xrs.xrs_ptr)->pr_un.pr_v8p.pr_filler);
29825c28e83SPiotr Jasiukajtis #endif
29925c28e83SPiotr Jasiukajtis 	gsr = (gsr >> 25) & 7;
30025c28e83SPiotr Jasiukajtis 	if (gsr & 4) {
30125c28e83SPiotr Jasiukajtis 		siamp = (void (*)()) siam[0];
30225c28e83SPiotr Jasiukajtis 		siamp();
30325c28e83SPiotr Jasiukajtis 		tmpfsr = fsr;
30425c28e83SPiotr Jasiukajtis 		fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
30525c28e83SPiotr Jasiukajtis 	}
30625c28e83SPiotr Jasiukajtis 	__fenv_setfsr(&fsr);
30725c28e83SPiotr Jasiukajtis 
30825c28e83SPiotr Jasiukajtis 	/* decode the operation */
30925c28e83SPiotr Jasiukajtis 	__fex_get_op(sip, uap, &info);
31025c28e83SPiotr Jasiukajtis 
31125c28e83SPiotr Jasiukajtis 	/* if a custom mode handler is installed, invoke it */
31225c28e83SPiotr Jasiukajtis 	if (mode == FEX_CUSTOM) {
31325c28e83SPiotr Jasiukajtis 		/* if we got here from feraiseexcept, pass dummy info */
31425c28e83SPiotr Jasiukajtis 		addr = (unsigned long)sip->si_addr;
31525c28e83SPiotr Jasiukajtis 		if (addr >= (unsigned long)feraiseexcept &&
31625c28e83SPiotr Jasiukajtis 		    addr < (unsigned long)fetestexcept) {
31725c28e83SPiotr Jasiukajtis 			info.op = fex_other;
31825c28e83SPiotr Jasiukajtis 			info.op1.type = info.op2.type = info.res.type =
31925c28e83SPiotr Jasiukajtis 			    fex_nodata;
32025c28e83SPiotr Jasiukajtis 		}
32125c28e83SPiotr Jasiukajtis 
32225c28e83SPiotr Jasiukajtis 		/* restore interval mode if it was set, and put the original
32325c28e83SPiotr Jasiukajtis 		   rounding direction and ns mode back in the fsr */
32425c28e83SPiotr Jasiukajtis 		if (gsr & 4) {
32525c28e83SPiotr Jasiukajtis 			__fenv_setfsr(&tmpfsr);
32625c28e83SPiotr Jasiukajtis 			siamp = (void (*)()) siam[1 + (gsr & 3)];
32725c28e83SPiotr Jasiukajtis 			siamp();
32825c28e83SPiotr Jasiukajtis 		}
32925c28e83SPiotr Jasiukajtis 
33025c28e83SPiotr Jasiukajtis 		handler(1 << (int)e, &info);
33125c28e83SPiotr Jasiukajtis 
33225c28e83SPiotr Jasiukajtis 		/* restore modes in case the user's handler changed them */
33325c28e83SPiotr Jasiukajtis 		if (gsr & 4) {
33425c28e83SPiotr Jasiukajtis 			siamp = (void (*)()) siam[0];
33525c28e83SPiotr Jasiukajtis 			siamp();
33625c28e83SPiotr Jasiukajtis 		}
33725c28e83SPiotr Jasiukajtis 		__fenv_setfsr(&fsr);
33825c28e83SPiotr Jasiukajtis 	}
33925c28e83SPiotr Jasiukajtis 
34025c28e83SPiotr Jasiukajtis 	/* stuff the result */
34125c28e83SPiotr Jasiukajtis 	__fex_st_result(sip, uap, &info);
34225c28e83SPiotr Jasiukajtis 
34325c28e83SPiotr Jasiukajtis 	/* "or" in any exception flags and update traps */
34425c28e83SPiotr Jasiukajtis 	fsr = uap->uc_mcontext.fpregs.fpu_fsr;
34525c28e83SPiotr Jasiukajtis 	fsr |= ((info.flags & 0x1f) << 5);
34625c28e83SPiotr Jasiukajtis 	i = __fex_te_needed(thr_handlers, fsr);
34725c28e83SPiotr Jasiukajtis 	__fenv_set_te(fsr, i);
34825c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fpu_fsr = fsr;
34925c28e83SPiotr Jasiukajtis 	return;
35025c28e83SPiotr Jasiukajtis 
35125c28e83SPiotr Jasiukajtis not_ieee:
35225c28e83SPiotr Jasiukajtis 	/* revert to the saved handler (if any) */
35325c28e83SPiotr Jasiukajtis 	mutex_lock(&hdlr_lock);
35425c28e83SPiotr Jasiukajtis 	act = oact;
35525c28e83SPiotr Jasiukajtis 	mutex_unlock(&hdlr_lock);
35625c28e83SPiotr Jasiukajtis 	switch ((unsigned long)act.sa_handler) {
35725c28e83SPiotr Jasiukajtis 	case (unsigned long)SIG_DFL:
35825c28e83SPiotr Jasiukajtis 		/* simulate trap with no handler installed */
35925c28e83SPiotr Jasiukajtis 		sigaction(SIGFPE, &act, NULL);
36025c28e83SPiotr Jasiukajtis 		kill(getpid(), SIGFPE);
36125c28e83SPiotr Jasiukajtis 		break;
36225c28e83SPiotr Jasiukajtis #if !defined(__lint)
36325c28e83SPiotr Jasiukajtis 	case (unsigned long)SIG_IGN:
36425c28e83SPiotr Jasiukajtis 		break;
36525c28e83SPiotr Jasiukajtis #endif
36625c28e83SPiotr Jasiukajtis 	default:
36725c28e83SPiotr Jasiukajtis 		act.sa_handler(sig, sip, uap);
36825c28e83SPiotr Jasiukajtis 	}
36925c28e83SPiotr Jasiukajtis }
37025c28e83SPiotr Jasiukajtis 
37125c28e83SPiotr Jasiukajtis #elif defined(__x86)
37225c28e83SPiotr Jasiukajtis 
37325c28e83SPiotr Jasiukajtis #if defined(__amd64)
37425c28e83SPiotr Jasiukajtis #define test_sse_hw	1
37525c28e83SPiotr Jasiukajtis #else
37625c28e83SPiotr Jasiukajtis extern int _sse_hw;
37725c28e83SPiotr Jasiukajtis #define test_sse_hw	_sse_hw
37825c28e83SPiotr Jasiukajtis #endif
37925c28e83SPiotr Jasiukajtis 
38025c28e83SPiotr Jasiukajtis #if !defined(REG_PC)
38125c28e83SPiotr Jasiukajtis #define REG_PC	EIP
38225c28e83SPiotr Jasiukajtis #endif
38325c28e83SPiotr Jasiukajtis 
38425c28e83SPiotr Jasiukajtis /*
38525c28e83SPiotr Jasiukajtis *  If a handling mode is in effect, apply it; otherwise invoke the
38625c28e83SPiotr Jasiukajtis *  saved handler
38725c28e83SPiotr Jasiukajtis */
38825c28e83SPiotr Jasiukajtis static void
__fex_hdlr(int sig,siginfo_t * sip,void * arg)389*30699046SRichard Lowe __fex_hdlr(int sig, siginfo_t *sip, void *arg)
39025c28e83SPiotr Jasiukajtis {
391*30699046SRichard Lowe 	ucontext_t		*uap = arg;
39225c28e83SPiotr Jasiukajtis 	struct fex_handler_data	*thr_handlers;
39325c28e83SPiotr Jasiukajtis 	struct sigaction	act;
39425c28e83SPiotr Jasiukajtis 	void			(*handler)() = NULL, (*simd_handler[4])();
39525c28e83SPiotr Jasiukajtis 	int			mode, simd_mode[4], i, len, accrued, *ap;
39625c28e83SPiotr Jasiukajtis 	unsigned int		cwsw, oldcwsw, mxcsr, oldmxcsr;
39725c28e83SPiotr Jasiukajtis 	enum fex_exception	e, simd_e[4];
39825c28e83SPiotr Jasiukajtis 	fex_info_t		info, simd_info[4];
39925c28e83SPiotr Jasiukajtis 	unsigned long		addr;
40025c28e83SPiotr Jasiukajtis 	siginfo_t		osip = *sip;
40125c28e83SPiotr Jasiukajtis 	sseinst_t		inst;
40225c28e83SPiotr Jasiukajtis 
40325c28e83SPiotr Jasiukajtis 	/* check for an exception caused by an SSE instruction */
40425c28e83SPiotr Jasiukajtis 	if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
40525c28e83SPiotr Jasiukajtis 		len = __fex_parse_sse(uap, &inst);
40625c28e83SPiotr Jasiukajtis 		if (len == 0)
40725c28e83SPiotr Jasiukajtis 			goto not_ieee;
40825c28e83SPiotr Jasiukajtis 
40925c28e83SPiotr Jasiukajtis 		/* disable all traps and clear flags */
41025c28e83SPiotr Jasiukajtis 		__fenv_getcwsw(&oldcwsw);
41125c28e83SPiotr Jasiukajtis 		cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
41225c28e83SPiotr Jasiukajtis 		__fenv_setcwsw(&cwsw);
41325c28e83SPiotr Jasiukajtis 		__fenv_getmxcsr(&oldmxcsr);
41425c28e83SPiotr Jasiukajtis 		mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
41525c28e83SPiotr Jasiukajtis 		__fenv_setmxcsr(&mxcsr);
41625c28e83SPiotr Jasiukajtis 
41725c28e83SPiotr Jasiukajtis 		if ((int)inst.op & SIMD) {
41825c28e83SPiotr Jasiukajtis 			__fex_get_simd_op(uap, &inst, simd_e, simd_info);
41925c28e83SPiotr Jasiukajtis 
42025c28e83SPiotr Jasiukajtis 			thr_handlers = __fex_get_thr_handlers();
42125c28e83SPiotr Jasiukajtis 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
42225c28e83SPiotr Jasiukajtis 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
42325c28e83SPiotr Jasiukajtis 			    fpchip_state.mxcsr;
42425c28e83SPiotr Jasiukajtis 
42525c28e83SPiotr Jasiukajtis 			e = (enum fex_exception)-1;
42625c28e83SPiotr Jasiukajtis 			mode = FEX_NONSTOP;
42725c28e83SPiotr Jasiukajtis 			for (i = 0; i < 4; i++) {
42825c28e83SPiotr Jasiukajtis 				if ((int)simd_e[i] < 0)
42925c28e83SPiotr Jasiukajtis 					continue;
43025c28e83SPiotr Jasiukajtis 
43125c28e83SPiotr Jasiukajtis 				e = simd_e[i];
43225c28e83SPiotr Jasiukajtis 				simd_mode[i] = FEX_NOHANDLER;
43325c28e83SPiotr Jasiukajtis 				simd_handler[i] = oact.sa_handler;
43425c28e83SPiotr Jasiukajtis 				if (thr_handlers &&
43525c28e83SPiotr Jasiukajtis 				    thr_handlers[(int)e].__mode !=
43625c28e83SPiotr Jasiukajtis 				    FEX_NOHANDLER) {
43725c28e83SPiotr Jasiukajtis 					simd_mode[i] =
43825c28e83SPiotr Jasiukajtis 					    thr_handlers[(int)e].__mode;
43925c28e83SPiotr Jasiukajtis 					simd_handler[i] =
44025c28e83SPiotr Jasiukajtis 					    thr_handlers[(int)e].__handler;
44125c28e83SPiotr Jasiukajtis 				}
44225c28e83SPiotr Jasiukajtis 				accrued &= ~te_bit[(int)e];
44325c28e83SPiotr Jasiukajtis 				switch (simd_mode[i]) {
44425c28e83SPiotr Jasiukajtis 				case FEX_ABORT:
44525c28e83SPiotr Jasiukajtis 					mode = FEX_ABORT;
44625c28e83SPiotr Jasiukajtis 					break;
44725c28e83SPiotr Jasiukajtis 				case FEX_SIGNAL:
44825c28e83SPiotr Jasiukajtis 					if (mode != FEX_ABORT)
44925c28e83SPiotr Jasiukajtis 						mode = FEX_SIGNAL;
45025c28e83SPiotr Jasiukajtis 					handler = simd_handler[i];
45125c28e83SPiotr Jasiukajtis 					break;
45225c28e83SPiotr Jasiukajtis 				case FEX_NOHANDLER:
45325c28e83SPiotr Jasiukajtis 					if (mode != FEX_ABORT && mode !=
45425c28e83SPiotr Jasiukajtis 					    FEX_SIGNAL)
45525c28e83SPiotr Jasiukajtis 						mode = FEX_NOHANDLER;
45625c28e83SPiotr Jasiukajtis 					break;
45725c28e83SPiotr Jasiukajtis 				}
45825c28e83SPiotr Jasiukajtis 			}
45925c28e83SPiotr Jasiukajtis 			if (e == (enum fex_exception)-1) {
46025c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
46125c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
46225c28e83SPiotr Jasiukajtis 				goto not_ieee;
46325c28e83SPiotr Jasiukajtis 			}
46425c28e83SPiotr Jasiukajtis 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
46525c28e83SPiotr Jasiukajtis 			    fpchip_state.status;
46625c28e83SPiotr Jasiukajtis 			ap = __fex_accrued();
46725c28e83SPiotr Jasiukajtis 			accrued |= *ap;
46825c28e83SPiotr Jasiukajtis 			accrued &= 0x3d;
46925c28e83SPiotr Jasiukajtis 
47025c28e83SPiotr Jasiukajtis 			for (i = 0; i < 4; i++) {
47125c28e83SPiotr Jasiukajtis 				if ((int)simd_e[i] < 0)
47225c28e83SPiotr Jasiukajtis 					continue;
47325c28e83SPiotr Jasiukajtis 
47425c28e83SPiotr Jasiukajtis 				__fex_mklog(uap, (char *)addr, accrued,
47525c28e83SPiotr Jasiukajtis 				    simd_e[i], simd_mode[i],
47625c28e83SPiotr Jasiukajtis 				    (void *)simd_handler[i]);
47725c28e83SPiotr Jasiukajtis 			}
47825c28e83SPiotr Jasiukajtis 
47925c28e83SPiotr Jasiukajtis 			if (mode == FEX_NOHANDLER) {
48025c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
48125c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
48225c28e83SPiotr Jasiukajtis 				goto not_ieee;
48325c28e83SPiotr Jasiukajtis 			} else if (mode == FEX_ABORT) {
48425c28e83SPiotr Jasiukajtis 				abort();
48525c28e83SPiotr Jasiukajtis 			} else if (mode == FEX_SIGNAL) {
48625c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
48725c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
48825c28e83SPiotr Jasiukajtis 				handler(sig, &osip, uap);
48925c28e83SPiotr Jasiukajtis 				return;
49025c28e83SPiotr Jasiukajtis 			}
49125c28e83SPiotr Jasiukajtis 
49225c28e83SPiotr Jasiukajtis 			*ap = 0;
49325c28e83SPiotr Jasiukajtis 			for (i = 0; i < 4; i++) {
49425c28e83SPiotr Jasiukajtis 				if ((int)simd_e[i] < 0)
49525c28e83SPiotr Jasiukajtis 					continue;
49625c28e83SPiotr Jasiukajtis 
49725c28e83SPiotr Jasiukajtis 				if (simd_mode[i] == FEX_CUSTOM) {
49825c28e83SPiotr Jasiukajtis 					handler(1 << (int)simd_e[i],
49925c28e83SPiotr Jasiukajtis 					    &simd_info[i]);
50025c28e83SPiotr Jasiukajtis 					__fenv_setcwsw(&cwsw);
50125c28e83SPiotr Jasiukajtis 					__fenv_setmxcsr(&mxcsr);
50225c28e83SPiotr Jasiukajtis 				}
50325c28e83SPiotr Jasiukajtis 			}
50425c28e83SPiotr Jasiukajtis 
50525c28e83SPiotr Jasiukajtis 			__fex_st_simd_result(uap, &inst, simd_e, simd_info);
50625c28e83SPiotr Jasiukajtis 			for (i = 0; i < 4; i++) {
50725c28e83SPiotr Jasiukajtis 				if ((int)simd_e[i] < 0)
50825c28e83SPiotr Jasiukajtis 					continue;
50925c28e83SPiotr Jasiukajtis 
51025c28e83SPiotr Jasiukajtis 				accrued |= simd_info[i].flags;
51125c28e83SPiotr Jasiukajtis 			}
51225c28e83SPiotr Jasiukajtis 
51325c28e83SPiotr Jasiukajtis 			if ((int)inst.op & INTREG) {
51425c28e83SPiotr Jasiukajtis 				/* set MMX mode */
51525c28e83SPiotr Jasiukajtis #if defined(__amd64)
51625c28e83SPiotr Jasiukajtis 				uap->uc_mcontext.fpregs.fp_reg_set.
51725c28e83SPiotr Jasiukajtis 				    fpchip_state.sw &= ~0x3800;
51825c28e83SPiotr Jasiukajtis 				uap->uc_mcontext.fpregs.fp_reg_set.
51925c28e83SPiotr Jasiukajtis 				    fpchip_state.fctw = 0;
52025c28e83SPiotr Jasiukajtis #else
52125c28e83SPiotr Jasiukajtis 				uap->uc_mcontext.fpregs.fp_reg_set.
52225c28e83SPiotr Jasiukajtis 				    fpchip_state.state[1] &= ~0x3800;
52325c28e83SPiotr Jasiukajtis 				uap->uc_mcontext.fpregs.fp_reg_set.
52425c28e83SPiotr Jasiukajtis 				    fpchip_state.state[2] = 0;
52525c28e83SPiotr Jasiukajtis #endif
52625c28e83SPiotr Jasiukajtis 			}
52725c28e83SPiotr Jasiukajtis 		} else {
52825c28e83SPiotr Jasiukajtis 			e = __fex_get_sse_op(uap, &inst, &info);
52925c28e83SPiotr Jasiukajtis 			if ((int)e < 0) {
53025c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
53125c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
53225c28e83SPiotr Jasiukajtis 				goto not_ieee;
53325c28e83SPiotr Jasiukajtis 			}
53425c28e83SPiotr Jasiukajtis 
53525c28e83SPiotr Jasiukajtis 			mode = FEX_NOHANDLER;
53625c28e83SPiotr Jasiukajtis 			handler = oact.sa_handler;
53725c28e83SPiotr Jasiukajtis 			thr_handlers = __fex_get_thr_handlers();
53825c28e83SPiotr Jasiukajtis 			if (thr_handlers && thr_handlers[(int)e].__mode !=
53925c28e83SPiotr Jasiukajtis 			    FEX_NOHANDLER) {
54025c28e83SPiotr Jasiukajtis 				mode = thr_handlers[(int)e].__mode;
54125c28e83SPiotr Jasiukajtis 				handler = thr_handlers[(int)e].__handler;
54225c28e83SPiotr Jasiukajtis 			}
54325c28e83SPiotr Jasiukajtis 
54425c28e83SPiotr Jasiukajtis 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
54525c28e83SPiotr Jasiukajtis 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
54625c28e83SPiotr Jasiukajtis 			    fpchip_state.mxcsr & ~te_bit[(int)e];
54725c28e83SPiotr Jasiukajtis 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
54825c28e83SPiotr Jasiukajtis 			    fpchip_state.status;
54925c28e83SPiotr Jasiukajtis 			ap = __fex_accrued();
55025c28e83SPiotr Jasiukajtis 			accrued |= *ap;
55125c28e83SPiotr Jasiukajtis 			accrued &= 0x3d;
55225c28e83SPiotr Jasiukajtis 			__fex_mklog(uap, (char *)addr, accrued, e, mode,
55325c28e83SPiotr Jasiukajtis 			    (void *)handler);
55425c28e83SPiotr Jasiukajtis 
55525c28e83SPiotr Jasiukajtis 			if (mode == FEX_NOHANDLER) {
55625c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
55725c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
55825c28e83SPiotr Jasiukajtis 				goto not_ieee;
55925c28e83SPiotr Jasiukajtis 			} else if (mode == FEX_ABORT) {
56025c28e83SPiotr Jasiukajtis 				abort();
56125c28e83SPiotr Jasiukajtis 			} else if (mode == FEX_SIGNAL) {
56225c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&oldcwsw);
56325c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&oldmxcsr);
56425c28e83SPiotr Jasiukajtis 				handler(sig, &osip, uap);
56525c28e83SPiotr Jasiukajtis 				return;
56625c28e83SPiotr Jasiukajtis 			} else if (mode == FEX_CUSTOM) {
56725c28e83SPiotr Jasiukajtis 				*ap = 0;
56825c28e83SPiotr Jasiukajtis 				if (addr >= (unsigned long)feraiseexcept &&
56925c28e83SPiotr Jasiukajtis 				    addr < (unsigned long)fetestexcept) {
57025c28e83SPiotr Jasiukajtis 					info.op = fex_other;
57125c28e83SPiotr Jasiukajtis 					info.op1.type = info.op2.type =
57225c28e83SPiotr Jasiukajtis 					    info.res.type = fex_nodata;
57325c28e83SPiotr Jasiukajtis 				}
57425c28e83SPiotr Jasiukajtis 				handler(1 << (int)e, &info);
57525c28e83SPiotr Jasiukajtis 				__fenv_setcwsw(&cwsw);
57625c28e83SPiotr Jasiukajtis 				__fenv_setmxcsr(&mxcsr);
57725c28e83SPiotr Jasiukajtis 			}
57825c28e83SPiotr Jasiukajtis 
57925c28e83SPiotr Jasiukajtis 			__fex_st_sse_result(uap, &inst, e, &info);
58025c28e83SPiotr Jasiukajtis 			accrued |= info.flags;
58125c28e83SPiotr Jasiukajtis 
58225c28e83SPiotr Jasiukajtis #if defined(__amd64)
58325c28e83SPiotr Jasiukajtis 			/*
58425c28e83SPiotr Jasiukajtis 			 * In 64-bit mode, the 32-bit convert-to-integer
58525c28e83SPiotr Jasiukajtis 			 * instructions zero the upper 32 bits of the
58625c28e83SPiotr Jasiukajtis 			 * destination.  (We do this here and not in
58725c28e83SPiotr Jasiukajtis 			 * __fex_st_sse_result because __fex_st_sse_result
58825c28e83SPiotr Jasiukajtis 			 * can be called from __fex_st_simd_result, too.)
58925c28e83SPiotr Jasiukajtis 			 */
59025c28e83SPiotr Jasiukajtis 			if (inst.op == cvtss2si || inst.op == cvttss2si ||
59125c28e83SPiotr Jasiukajtis 			    inst.op == cvtsd2si || inst.op == cvttsd2si)
59225c28e83SPiotr Jasiukajtis 				inst.op1->i[1] = 0;
59325c28e83SPiotr Jasiukajtis #endif
59425c28e83SPiotr Jasiukajtis 		}
59525c28e83SPiotr Jasiukajtis 
59625c28e83SPiotr Jasiukajtis 		/* advance the pc past the SSE instruction */
59725c28e83SPiotr Jasiukajtis 		uap->uc_mcontext.gregs[REG_PC] += len;
59825c28e83SPiotr Jasiukajtis 		goto update_state;
59925c28e83SPiotr Jasiukajtis 	}
60025c28e83SPiotr Jasiukajtis 
60125c28e83SPiotr Jasiukajtis 	/* determine which exception occurred */
60225c28e83SPiotr Jasiukajtis 	__fex_get_x86_exc(sip, uap);
60325c28e83SPiotr Jasiukajtis 	switch (sip->si_code) {
60425c28e83SPiotr Jasiukajtis 	case FPE_FLTDIV:
60525c28e83SPiotr Jasiukajtis 		e = fex_division;
60625c28e83SPiotr Jasiukajtis 		break;
60725c28e83SPiotr Jasiukajtis 	case FPE_FLTOVF:
60825c28e83SPiotr Jasiukajtis 		e = fex_overflow;
60925c28e83SPiotr Jasiukajtis 		break;
61025c28e83SPiotr Jasiukajtis 	case FPE_FLTUND:
61125c28e83SPiotr Jasiukajtis 		e = fex_underflow;
61225c28e83SPiotr Jasiukajtis 		break;
61325c28e83SPiotr Jasiukajtis 	case FPE_FLTRES:
61425c28e83SPiotr Jasiukajtis 		e = fex_inexact;
61525c28e83SPiotr Jasiukajtis 		break;
61625c28e83SPiotr Jasiukajtis 	case FPE_FLTINV:
61725c28e83SPiotr Jasiukajtis 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
61825c28e83SPiotr Jasiukajtis 			goto not_ieee;
61925c28e83SPiotr Jasiukajtis 		break;
62025c28e83SPiotr Jasiukajtis 	default:
62125c28e83SPiotr Jasiukajtis 		/* not an IEEE exception */
62225c28e83SPiotr Jasiukajtis 		goto not_ieee;
62325c28e83SPiotr Jasiukajtis 	}
62425c28e83SPiotr Jasiukajtis 
62525c28e83SPiotr Jasiukajtis 	/* get the handling mode */
62625c28e83SPiotr Jasiukajtis 	mode = FEX_NOHANDLER;
62725c28e83SPiotr Jasiukajtis 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
62825c28e83SPiotr Jasiukajtis 	thr_handlers = __fex_get_thr_handlers();
62925c28e83SPiotr Jasiukajtis 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
63025c28e83SPiotr Jasiukajtis 		mode = thr_handlers[(int)e].__mode;
63125c28e83SPiotr Jasiukajtis 		handler = thr_handlers[(int)e].__handler;
63225c28e83SPiotr Jasiukajtis 	}
63325c28e83SPiotr Jasiukajtis 
63425c28e83SPiotr Jasiukajtis 	/* make an entry in the log of retro. diag. if need be */
63525c28e83SPiotr Jasiukajtis #if defined(__amd64)
63625c28e83SPiotr Jasiukajtis 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
63725c28e83SPiotr Jasiukajtis 	    fpchip_state.rip;
63825c28e83SPiotr Jasiukajtis #else
63925c28e83SPiotr Jasiukajtis 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
64025c28e83SPiotr Jasiukajtis 	    fpchip_state.state[3];
64125c28e83SPiotr Jasiukajtis #endif
64255fea89dSDan Cross 	accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status &
64325c28e83SPiotr Jasiukajtis 	    ~te_bit[(int)e];
64425c28e83SPiotr Jasiukajtis 	if (test_sse_hw)
64525c28e83SPiotr Jasiukajtis 		accrued |= uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
64625c28e83SPiotr Jasiukajtis 		    mxcsr;
64725c28e83SPiotr Jasiukajtis 	ap = __fex_accrued();
64825c28e83SPiotr Jasiukajtis 	accrued |= *ap;
64925c28e83SPiotr Jasiukajtis 	accrued &= 0x3d;
65025c28e83SPiotr Jasiukajtis 	__fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
65125c28e83SPiotr Jasiukajtis 
65225c28e83SPiotr Jasiukajtis 	/* handle the exception based on the mode */
65325c28e83SPiotr Jasiukajtis 	if (mode == FEX_NOHANDLER)
65425c28e83SPiotr Jasiukajtis 		goto not_ieee;
65525c28e83SPiotr Jasiukajtis 	else if (mode == FEX_ABORT)
65625c28e83SPiotr Jasiukajtis 		abort();
65725c28e83SPiotr Jasiukajtis 	else if (mode == FEX_SIGNAL) {
65825c28e83SPiotr Jasiukajtis 		handler(sig, &osip, uap);
65925c28e83SPiotr Jasiukajtis 		return;
66025c28e83SPiotr Jasiukajtis 	}
66125c28e83SPiotr Jasiukajtis 
66225c28e83SPiotr Jasiukajtis 	/* disable all traps and clear flags */
66325c28e83SPiotr Jasiukajtis 	__fenv_getcwsw(&cwsw);
66425c28e83SPiotr Jasiukajtis 	cwsw = (cwsw & ~0x3f) | 0x003f0000;
66525c28e83SPiotr Jasiukajtis 	__fenv_setcwsw(&cwsw);
66625c28e83SPiotr Jasiukajtis 	if (test_sse_hw) {
66725c28e83SPiotr Jasiukajtis 		__fenv_getmxcsr(&mxcsr);
66825c28e83SPiotr Jasiukajtis 		mxcsr = (mxcsr & ~0x3f) | 0x1f80;
66925c28e83SPiotr Jasiukajtis 		__fenv_setmxcsr(&mxcsr);
67025c28e83SPiotr Jasiukajtis 	}
67125c28e83SPiotr Jasiukajtis 	*ap = 0;
67225c28e83SPiotr Jasiukajtis 
67325c28e83SPiotr Jasiukajtis 	/* decode the operation */
67425c28e83SPiotr Jasiukajtis 	__fex_get_op(sip, uap, &info);
67525c28e83SPiotr Jasiukajtis 
67625c28e83SPiotr Jasiukajtis 	/* if a custom mode handler is installed, invoke it */
67725c28e83SPiotr Jasiukajtis 	if (mode == FEX_CUSTOM) {
67825c28e83SPiotr Jasiukajtis 		/* if we got here from feraiseexcept, pass dummy info */
67925c28e83SPiotr Jasiukajtis 		if (addr >= (unsigned long)feraiseexcept &&
68025c28e83SPiotr Jasiukajtis 		    addr < (unsigned long)fetestexcept) {
68125c28e83SPiotr Jasiukajtis 			info.op = fex_other;
68225c28e83SPiotr Jasiukajtis 			info.op1.type = info.op2.type = info.res.type =
68325c28e83SPiotr Jasiukajtis 			    fex_nodata;
68425c28e83SPiotr Jasiukajtis 		}
68525c28e83SPiotr Jasiukajtis 
68625c28e83SPiotr Jasiukajtis 		handler(1 << (int)e, &info);
68725c28e83SPiotr Jasiukajtis 
68825c28e83SPiotr Jasiukajtis 		/* restore modes in case the user's handler changed them */
68925c28e83SPiotr Jasiukajtis 		__fenv_setcwsw(&cwsw);
69025c28e83SPiotr Jasiukajtis 		if (test_sse_hw)
69125c28e83SPiotr Jasiukajtis 			__fenv_setmxcsr(&mxcsr);
69225c28e83SPiotr Jasiukajtis 	}
69325c28e83SPiotr Jasiukajtis 
69425c28e83SPiotr Jasiukajtis 	/* stuff the result */
69525c28e83SPiotr Jasiukajtis 	__fex_st_result(sip, uap, &info);
69625c28e83SPiotr Jasiukajtis 	accrued |= info.flags;
69725c28e83SPiotr Jasiukajtis 
69825c28e83SPiotr Jasiukajtis update_state:
69925c28e83SPiotr Jasiukajtis 	accrued &= 0x3d;
70025c28e83SPiotr Jasiukajtis 	i = __fex_te_needed(thr_handlers, accrued);
70125c28e83SPiotr Jasiukajtis 	*ap = accrued & i;
70225c28e83SPiotr Jasiukajtis #if defined(__amd64)
70325c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
70425c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
70525c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
70625c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
70725c28e83SPiotr Jasiukajtis #else
70825c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
70925c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |=
71025c28e83SPiotr Jasiukajtis 	    (accrued & ~i);
71125c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
71225c28e83SPiotr Jasiukajtis 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
71325c28e83SPiotr Jasiukajtis #endif
71425c28e83SPiotr Jasiukajtis 	if (test_sse_hw) {
71525c28e83SPiotr Jasiukajtis 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
71625c28e83SPiotr Jasiukajtis 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
71725c28e83SPiotr Jasiukajtis 		    0x1e80 | (accrued & ~i);
71825c28e83SPiotr Jasiukajtis 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &=
71925c28e83SPiotr Jasiukajtis 		    ~(i << 7);
72025c28e83SPiotr Jasiukajtis 	}
72125c28e83SPiotr Jasiukajtis 	return;
72225c28e83SPiotr Jasiukajtis 
72325c28e83SPiotr Jasiukajtis not_ieee:
72425c28e83SPiotr Jasiukajtis 	/* revert to the saved handler (if any) */
72525c28e83SPiotr Jasiukajtis 	mutex_lock(&hdlr_lock);
72625c28e83SPiotr Jasiukajtis 	act = oact;
72725c28e83SPiotr Jasiukajtis 	mutex_unlock(&hdlr_lock);
72825c28e83SPiotr Jasiukajtis 	switch ((unsigned long)act.sa_handler) {
72925c28e83SPiotr Jasiukajtis 	case (unsigned long)SIG_DFL:
73025c28e83SPiotr Jasiukajtis 		/* simulate trap with no handler installed */
73125c28e83SPiotr Jasiukajtis 		sigaction(SIGFPE, &act, NULL);
73225c28e83SPiotr Jasiukajtis 		kill(getpid(), SIGFPE);
73325c28e83SPiotr Jasiukajtis 		break;
73425c28e83SPiotr Jasiukajtis #if !defined(__lint)
73525c28e83SPiotr Jasiukajtis 	case (unsigned long)SIG_IGN:
73625c28e83SPiotr Jasiukajtis 		break;
73725c28e83SPiotr Jasiukajtis #endif
73825c28e83SPiotr Jasiukajtis 	default:
739*30699046SRichard Lowe 		act.sa_sigaction(sig, &osip, uap);
74025c28e83SPiotr Jasiukajtis 	}
74125c28e83SPiotr Jasiukajtis }
74225c28e83SPiotr Jasiukajtis 
74325c28e83SPiotr Jasiukajtis #else
74425c28e83SPiotr Jasiukajtis #error Unknown architecture
74525c28e83SPiotr Jasiukajtis #endif
74625c28e83SPiotr Jasiukajtis 
74725c28e83SPiotr Jasiukajtis /*
74825c28e83SPiotr Jasiukajtis *  Return a pointer to the thread-specific handler data, and
74925c28e83SPiotr Jasiukajtis *  initialize it if necessary
75025c28e83SPiotr Jasiukajtis */
75125c28e83SPiotr Jasiukajtis struct fex_handler_data *
__fex_get_thr_handlers()75225c28e83SPiotr Jasiukajtis __fex_get_thr_handlers()
75325c28e83SPiotr Jasiukajtis {
75425c28e83SPiotr Jasiukajtis 	struct fex_handler_data	*ptr;
75525c28e83SPiotr Jasiukajtis 	unsigned long			fsr;
75625c28e83SPiotr Jasiukajtis 	int						i, te;
75725c28e83SPiotr Jasiukajtis 
75825c28e83SPiotr Jasiukajtis 	if (thr_main()) {
75925c28e83SPiotr Jasiukajtis 		if (!handlers_initialized) {
76025c28e83SPiotr Jasiukajtis 			/* initialize to FEX_NOHANDLER if trap is enabled,
76125c28e83SPiotr Jasiukajtis 			   FEX_NONSTOP if trap is disabled */
76225c28e83SPiotr Jasiukajtis 			__fenv_getfsr(&fsr);
76325c28e83SPiotr Jasiukajtis 			te = (int)__fenv_get_te(fsr);
76425c28e83SPiotr Jasiukajtis 			for (i = 0; i < FEX_NUM_EXC; i++)
76525c28e83SPiotr Jasiukajtis 				main_handlers[i].__mode =
76625c28e83SPiotr Jasiukajtis 					((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
76725c28e83SPiotr Jasiukajtis 			handlers_initialized = 1;
76825c28e83SPiotr Jasiukajtis 		}
76925c28e83SPiotr Jasiukajtis 		return main_handlers;
77025c28e83SPiotr Jasiukajtis 	}
77125c28e83SPiotr Jasiukajtis 	else {
77225c28e83SPiotr Jasiukajtis 		ptr = NULL;
77325c28e83SPiotr Jasiukajtis 		mutex_lock(&handlers_key_lock);
77425c28e83SPiotr Jasiukajtis 		if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
77525c28e83SPiotr Jasiukajtis 			thr_keycreate(&handlers_key, free) != 0) {
77625c28e83SPiotr Jasiukajtis 			mutex_unlock(&handlers_key_lock);
77725c28e83SPiotr Jasiukajtis 			return NULL;
77825c28e83SPiotr Jasiukajtis 		}
77925c28e83SPiotr Jasiukajtis 		mutex_unlock(&handlers_key_lock);
78025c28e83SPiotr Jasiukajtis 		if (!ptr) {
78125c28e83SPiotr Jasiukajtis 			if ((ptr = (struct fex_handler_data *)
78225c28e83SPiotr Jasiukajtis 				malloc(sizeof(fex_handler_t))) == NULL) {
78325c28e83SPiotr Jasiukajtis 				return NULL;
78425c28e83SPiotr Jasiukajtis 			}
78525c28e83SPiotr Jasiukajtis 			if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
78625c28e83SPiotr Jasiukajtis 				(void)free(ptr);
78725c28e83SPiotr Jasiukajtis 				return NULL;
78825c28e83SPiotr Jasiukajtis 			}
78925c28e83SPiotr Jasiukajtis 			/* initialize to FEX_NOHANDLER if trap is enabled,
79025c28e83SPiotr Jasiukajtis 			   FEX_NONSTOP if trap is disabled */
79125c28e83SPiotr Jasiukajtis 			__fenv_getfsr(&fsr);
79225c28e83SPiotr Jasiukajtis 			te = (int)__fenv_get_te(fsr);
79325c28e83SPiotr Jasiukajtis 			for (i = 0; i < FEX_NUM_EXC; i++)
79425c28e83SPiotr Jasiukajtis 				ptr[i].__mode = ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
79525c28e83SPiotr Jasiukajtis 		}
79625c28e83SPiotr Jasiukajtis 		return ptr;
79725c28e83SPiotr Jasiukajtis 	}
79825c28e83SPiotr Jasiukajtis }
79925c28e83SPiotr Jasiukajtis 
80025c28e83SPiotr Jasiukajtis /*
80125c28e83SPiotr Jasiukajtis *  Update the trap enable bits according to the selected modes
80225c28e83SPiotr Jasiukajtis */
80325c28e83SPiotr Jasiukajtis void
__fex_update_te()80425c28e83SPiotr Jasiukajtis __fex_update_te()
80525c28e83SPiotr Jasiukajtis {
80625c28e83SPiotr Jasiukajtis 	struct fex_handler_data	*thr_handlers;
80725c28e83SPiotr Jasiukajtis 	struct sigaction		act, tmpact;
80825c28e83SPiotr Jasiukajtis 	sigset_t				blocked;
80925c28e83SPiotr Jasiukajtis 	unsigned long			fsr;
81025c28e83SPiotr Jasiukajtis 	int						te;
81125c28e83SPiotr Jasiukajtis 
81225c28e83SPiotr Jasiukajtis 	/* determine which traps are needed */
81325c28e83SPiotr Jasiukajtis 	thr_handlers = __fex_get_thr_handlers();
81425c28e83SPiotr Jasiukajtis 	__fenv_getfsr(&fsr);
81525c28e83SPiotr Jasiukajtis 	te = __fex_te_needed(thr_handlers, fsr);
81625c28e83SPiotr Jasiukajtis 
81725c28e83SPiotr Jasiukajtis 	/* install __fex_hdlr as necessary */
81825c28e83SPiotr Jasiukajtis 	if (!hdlr_installed && te) {
819*30699046SRichard Lowe 		act.sa_sigaction = __fex_hdlr;
82025c28e83SPiotr Jasiukajtis 		sigemptyset(&act.sa_mask);
82125c28e83SPiotr Jasiukajtis 		act.sa_flags = SA_SIGINFO;
82225c28e83SPiotr Jasiukajtis 		sigaction(SIGFPE, &act, &tmpact);
823*30699046SRichard Lowe 		if (tmpact.sa_sigaction != __fex_hdlr)
82425c28e83SPiotr Jasiukajtis 		{
82525c28e83SPiotr Jasiukajtis 			mutex_lock(&hdlr_lock);
82625c28e83SPiotr Jasiukajtis 			oact = tmpact;
82725c28e83SPiotr Jasiukajtis 			mutex_unlock(&hdlr_lock);
82825c28e83SPiotr Jasiukajtis 		}
82925c28e83SPiotr Jasiukajtis 		hdlr_installed = 1;
83025c28e83SPiotr Jasiukajtis 	}
83125c28e83SPiotr Jasiukajtis 
83225c28e83SPiotr Jasiukajtis 	/* set the new trap enable bits (only if SIGFPE is not blocked) */
83325c28e83SPiotr Jasiukajtis 	if (sigprocmask(0, NULL, &blocked) == 0 &&
83425c28e83SPiotr Jasiukajtis 		!sigismember(&blocked, SIGFPE)) {
83525c28e83SPiotr Jasiukajtis 		__fenv_set_te(fsr, te);
83625c28e83SPiotr Jasiukajtis 		__fenv_setfsr(&fsr);
83725c28e83SPiotr Jasiukajtis 	}
83825c28e83SPiotr Jasiukajtis 
83925c28e83SPiotr Jasiukajtis 	/* synchronize with libmtsk */
84025c28e83SPiotr Jasiukajtis 	__mt_fex_sync = __fex_sync_with_libmtsk;
84125c28e83SPiotr Jasiukajtis 
84225c28e83SPiotr Jasiukajtis 	/* synchronize with other projects */
84325c28e83SPiotr Jasiukajtis 	__libm_mt_fex_sync = __fex_sync_with_threads;
84425c28e83SPiotr Jasiukajtis }
845