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