2005-04-17 02:20:36 +04:00
/*
* 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
*/
2009-04-06 13:29:58 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# 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 :
2006-01-12 01:11:30 +03:00
case OPC_2E_INVALIDEXCEPTION :
2005-04-17 02:20:36 +04:00
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 ) ;
2010-05-04 00:44:21 +04:00
Clear_excp_register ( exception_index ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
}