kernel_optimize_test/arch/parisc/math-emu/decode_exc.c
Helge Deller 550f0d9222 parisc: clear floating point exception flag on SIGFPE signal
Clear the floating point exception flag before returning to
user space. This is needed, else the libc trampoline handler
may hit the same SIGFPE again while building up a trampoline
to a signal handler.

Fixes debian bug #559406.

Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Kyle McMartin <kyle@mcmartin.ca>
2010-05-30 05:39:58 -04:00

371 lines
11 KiB
C

/*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
*
* Floating-point emulation code
* Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* BEGIN_DESC
*
* File:
* @(#) pa/fp/decode_exc.c $ Revision: $
*
* Purpose:
* <<please update with a synopsis of the functionality provided by this file>>
*
* External Interfaces:
* <<the following list was autogenerated, please review>>
* decode_fpu(Fpu_register, trap_counts)
*
* Internal Interfaces:
* <<please update>>
*
* Theory:
* <<please update with a overview of the operation of this file>>
*
* END_DESC
*/
#include <linux/kernel.h>
#include "float.h"
#include "sgl_float.h"
#include "dbl_float.h"
#include "cnv_float.h"
/* #include "types.h" */
#include <asm/signal.h>
#include <asm/siginfo.h>
/* #include <machine/sys/mdep_private.h> */
#undef Fpustatus_register
#define Fpustatus_register Fpu_register[0]
/* General definitions */
#define DOESTRAP 1
#define NOTRAP 0
#define SIGNALCODE(signal, code) ((signal) << 24 | (code));
#define copropbit 1<<31-2 /* bit position 2 */
#define opclass 9 /* bits 21 & 22 */
#define fmt 11 /* bits 19 & 20 */
#define df 13 /* bits 17 & 18 */
#define twobits 3 /* mask low-order 2 bits */
#define fivebits 31 /* mask low-order 5 bits */
#define MAX_EXCP_REG 7 /* number of excpeption registers to check */
/* Exception register definitions */
#define Excp_type(index) Exceptiontype(Fpu_register[index])
#define Excp_instr(index) Instructionfield(Fpu_register[index])
#define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0
#define Excp_format() \
(current_ir >> ((current_ir>>opclass & twobits)==1 ? df : fmt) & twobits)
/* Miscellaneous definitions */
#define Fpu_sgl(index) Fpu_register[index*2]
#define Fpu_dblp1(index) Fpu_register[index*2]
#define Fpu_dblp2(index) Fpu_register[(index*2)+1]
#define Fpu_quadp1(index) Fpu_register[index*2]
#define Fpu_quadp2(index) Fpu_register[(index*2)+1]
#define Fpu_quadp3(index) Fpu_register[(index*2)+2]
#define Fpu_quadp4(index) Fpu_register[(index*2)+3]
/* Single precision floating-point definitions */
#ifndef Sgl_decrement
# define Sgl_decrement(sgl_value) Sall(sgl_value)--
#endif
/* Double precision floating-point definitions */
#ifndef Dbl_decrement
# define Dbl_decrement(dbl_valuep1,dbl_valuep2) \
if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)--
#endif
#define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \
aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \
Fpu_register[0] |= bflags; \
}
u_int
decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[])
{
unsigned int current_ir, excp;
int target, exception_index = 1;
boolean inexact;
unsigned int aflags;
unsigned int bflags;
unsigned int excptype;
/* Keep stats on how many floating point exceptions (based on type)
* that happen. Want to keep this overhead low, but still provide
* some information to the customer. All exits from this routine
* need to restore Fpu_register[0]
*/
bflags=(Fpu_register[0] & 0xf8000000);
Fpu_register[0] &= 0x07ffffff;
/* exception_index is used to index the exception register queue. It
* always points at the last register that contains a valid exception. A
* zero value implies no exceptions (also the initialized value). Setting
* the T-bit resets the exception_index to zero.
*/
/*
* Check for reserved-op exception. A reserved-op exception does not
* set any exception registers nor does it set the T-bit. If the T-bit
* is not set then a reserved-op exception occurred.
*
* At some point, we may want to report reserved op exceptions as
* illegal instructions.
*/
if (!Is_tbit_set()) {
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
return SIGNALCODE(SIGILL, ILL_COPROC);
}
/*
* Is a coprocessor op.
*
* Now we need to determine what type of exception occurred.
*/
for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) {
current_ir = Excp_instr(exception_index);
/*
* On PA89: there are 5 different unimplemented exception
* codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds
* another, 0x2b. Only these have the low order bit set.
*/
excptype = Excp_type(exception_index);
if (excptype & UNIMPLEMENTEDEXCEPTION) {
/*
* Clear T-bit and exception register so that
* we can tell if a trap really occurs while
* emulating the instruction.
*/
Clear_tbit();
Clear_excp_register(exception_index);
/*
* Now emulate this instruction. If a trap occurs,
* fpudispatch will return a non-zero number
*/
excp = fpudispatch(current_ir,excptype,0,Fpu_register);
/* accumulate the status flags, don't lose them as in hpux */
if (excp) {
/*
* We now need to make sure that the T-bit and the
* exception register contain the correct values
* before continuing.
*/
/*
* Set t-bit since it might still be needed for a
* subsequent real trap (I don't understand fully -PB)
*/
Set_tbit();
/* some of the following code uses
* Excp_type(exception_index) so fix that up */
Set_exceptiontype_and_instr_field(excp,current_ir,
Fpu_register[exception_index]);
if (excp == UNIMPLEMENTEDEXCEPTION) {
/*
* it is really unimplemented, so restore the
* TIMEX extended unimplemented exception code
*/
excp = excptype;
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
return SIGNALCODE(SIGILL, ILL_COPROC);
}
/* some of the following code uses excptype, so
* fix that up too */
excptype = excp;
}
/* handle exceptions other than the real UNIMPLIMENTED the
* same way as if the hardware had caused them */
if (excp == NOEXCEPTION)
/* For now use 'break', should technically be 'continue' */
break;
}
/*
* In PA89, the underflow exception has been extended to encode
* additional information. The exception looks like pp01x0,
* where x is 1 if inexact and pp represent the inexact bit (I)
* and the round away bit (RA)
*/
if (excptype & UNDERFLOWEXCEPTION) {
/* check for underflow trap enabled */
if (Is_underflowtrap_enabled()) {
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTUND);
} else {
/*
* Isn't a real trap; we need to
* return the default value.
*/
target = current_ir & fivebits;
#ifndef lint
if (Ibit(Fpu_register[exception_index])) inexact = TRUE;
else inexact = FALSE;
#endif
switch (Excp_format()) {
case SGL:
/*
* If ra (round-away) is set, will
* want to undo the rounding done
* by the hardware.
*/
if (Rabit(Fpu_register[exception_index]))
Sgl_decrement(Fpu_sgl(target));
/* now denormalize */
sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode());
break;
case DBL:
/*
* If ra (round-away) is set, will
* want to undo the rounding done
* by the hardware.
*/
if (Rabit(Fpu_register[exception_index]))
Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target));
/* now denormalize */
dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target),
&inexact,Rounding_mode());
break;
}
if (inexact) Set_underflowflag();
/*
* Underflow can generate an inexact
* exception. If inexact trap is enabled,
* want to do an inexact trap, otherwise
* set inexact flag.
*/
if (inexact && Is_inexacttrap_enabled()) {
/*
* Set exception field of exception register
* to inexact, parm field to zero.
* Underflow bit should be cleared.
*/
Set_exceptiontype(Fpu_register[exception_index],
INEXACTEXCEPTION);
Set_parmfield(Fpu_register[exception_index],0);
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTRES);
}
else {
/*
* Exception register needs to be cleared.
* Inexact flag needs to be set if inexact.
*/
Clear_excp_register(exception_index);
if (inexact) Set_inexactflag();
}
}
continue;
}
switch(Excp_type(exception_index)) {
case OVERFLOWEXCEPTION:
case OVERFLOWEXCEPTION | INEXACTEXCEPTION:
/* check for overflow trap enabled */
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
if (Is_overflowtrap_enabled()) {
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTOVF);
} else {
/*
* Isn't a real trap; we need to
* return the default value.
*/
target = current_ir & fivebits;
switch (Excp_format()) {
case SGL:
Sgl_setoverflow(Fpu_sgl(target));
break;
case DBL:
Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target));
break;
}
Set_overflowflag();
/*
* Overflow always generates an inexact
* exception. If inexact trap is enabled,
* want to do an inexact trap, otherwise
* set inexact flag.
*/
if (Is_inexacttrap_enabled()) {
/*
* Set exception field of exception
* register to inexact. Overflow
* bit should be cleared.
*/
Set_exceptiontype(Fpu_register[exception_index],
INEXACTEXCEPTION);
update_trap_counts(Fpu_register, aflags, bflags,
trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTRES);
}
else {
/*
* Exception register needs to be cleared.
* Inexact flag needs to be set.
*/
Clear_excp_register(exception_index);
Set_inexactflag();
}
}
break;
case INVALIDEXCEPTION:
case OPC_2E_INVALIDEXCEPTION:
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTINV);
case DIVISIONBYZEROEXCEPTION:
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
Clear_excp_register(exception_index);
return SIGNALCODE(SIGFPE, FPE_FLTDIV);
case INEXACTEXCEPTION:
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
return SIGNALCODE(SIGFPE, FPE_FLTRES);
default:
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__,
__LINE__, Excp_type(exception_index));
return SIGNALCODE(SIGILL, ILL_COPROC);
case NOEXCEPTION: /* no exception */
/*
* Clear exception register in case
* other fields are non-zero.
*/
Clear_excp_register(exception_index);
break;
}
}
/*
* No real exceptions occurred.
*/
Clear_tbit();
update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
return(NOTRAP);
}