162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux/PA-RISC Project (http://www.parisc-linux.org/) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Floating-point emulation code 662306a36Sopenharmony_ci * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * BEGIN_DESC 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * File: 1262306a36Sopenharmony_ci * @(#) pa/fp/decode_exc.c $ Revision: $ 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Purpose: 1562306a36Sopenharmony_ci * <<please update with a synopsis of the functionality provided by this file>> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * External Interfaces: 1862306a36Sopenharmony_ci * <<the following list was autogenerated, please review>> 1962306a36Sopenharmony_ci * decode_fpu(Fpu_register, trap_counts) 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Internal Interfaces: 2262306a36Sopenharmony_ci * <<please update>> 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Theory: 2562306a36Sopenharmony_ci * <<please update with a overview of the operation of this file>> 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * END_DESC 2862306a36Sopenharmony_ci*/ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/kernel.h> 3162306a36Sopenharmony_ci#include "float.h" 3262306a36Sopenharmony_ci#include "sgl_float.h" 3362306a36Sopenharmony_ci#include "dbl_float.h" 3462306a36Sopenharmony_ci#include "cnv_float.h" 3562306a36Sopenharmony_ci/* #include "types.h" */ 3662306a36Sopenharmony_ci#include <asm/signal.h> 3762306a36Sopenharmony_ci#include <asm/siginfo.h> 3862306a36Sopenharmony_ci/* #include <machine/sys/mdep_private.h> */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#undef Fpustatus_register 4162306a36Sopenharmony_ci#define Fpustatus_register Fpu_register[0] 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* General definitions */ 4462306a36Sopenharmony_ci#define DOESTRAP 1 4562306a36Sopenharmony_ci#define NOTRAP 0 4662306a36Sopenharmony_ci#define SIGNALCODE(signal, code) ((signal) << 24 | (code)) 4762306a36Sopenharmony_ci#define copropbit 1<<31-2 /* bit position 2 */ 4862306a36Sopenharmony_ci#define opclass 9 /* bits 21 & 22 */ 4962306a36Sopenharmony_ci#define fmtbits 11 /* bits 19 & 20 */ 5062306a36Sopenharmony_ci#define df 13 /* bits 17 & 18 */ 5162306a36Sopenharmony_ci#define twobits 3 /* mask low-order 2 bits */ 5262306a36Sopenharmony_ci#define fivebits 31 /* mask low-order 5 bits */ 5362306a36Sopenharmony_ci#define MAX_EXCP_REG 7 /* number of excpeption registers to check */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Exception register definitions */ 5662306a36Sopenharmony_ci#define Excp_type(index) Exceptiontype(Fpu_register[index]) 5762306a36Sopenharmony_ci#define Excp_instr(index) Instructionfield(Fpu_register[index]) 5862306a36Sopenharmony_ci#define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0 5962306a36Sopenharmony_ci#define Excp_format() \ 6062306a36Sopenharmony_ci (current_ir >> ((current_ir>>opclass & twobits) == 1 ? df : fmtbits) & twobits) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Miscellaneous definitions */ 6362306a36Sopenharmony_ci#define Fpu_sgl(index) Fpu_register[index*2] 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define Fpu_dblp1(index) Fpu_register[index*2] 6662306a36Sopenharmony_ci#define Fpu_dblp2(index) Fpu_register[(index*2)+1] 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define Fpu_quadp1(index) Fpu_register[index*2] 6962306a36Sopenharmony_ci#define Fpu_quadp2(index) Fpu_register[(index*2)+1] 7062306a36Sopenharmony_ci#define Fpu_quadp3(index) Fpu_register[(index*2)+2] 7162306a36Sopenharmony_ci#define Fpu_quadp4(index) Fpu_register[(index*2)+3] 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Single precision floating-point definitions */ 7462306a36Sopenharmony_ci#ifndef Sgl_decrement 7562306a36Sopenharmony_ci# define Sgl_decrement(sgl_value) Sall(sgl_value)-- 7662306a36Sopenharmony_ci#endif 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Double precision floating-point definitions */ 7962306a36Sopenharmony_ci#ifndef Dbl_decrement 8062306a36Sopenharmony_ci# define Dbl_decrement(dbl_valuep1,dbl_valuep2) \ 8162306a36Sopenharmony_ci if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)-- 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) { \ 8662306a36Sopenharmony_ci aflags=(Fpu_register[0])>>27; /* assumes zero fill. 32 bit */ \ 8762306a36Sopenharmony_ci Fpu_register[0] |= bflags; \ 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciu_int 9162306a36Sopenharmony_cidecode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[]) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned int current_ir, excp; 9462306a36Sopenharmony_ci int target, exception_index = 1; 9562306a36Sopenharmony_ci boolean inexact; 9662306a36Sopenharmony_ci unsigned int aflags; 9762306a36Sopenharmony_ci unsigned int bflags; 9862306a36Sopenharmony_ci unsigned int excptype; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Keep stats on how many floating point exceptions (based on type) 10262306a36Sopenharmony_ci * that happen. Want to keep this overhead low, but still provide 10362306a36Sopenharmony_ci * some information to the customer. All exits from this routine 10462306a36Sopenharmony_ci * need to restore Fpu_register[0] 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci bflags=(Fpu_register[0] & 0xf8000000); 10862306a36Sopenharmony_ci Fpu_register[0] &= 0x07ffffff; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* exception_index is used to index the exception register queue. It 11162306a36Sopenharmony_ci * always points at the last register that contains a valid exception. A 11262306a36Sopenharmony_ci * zero value implies no exceptions (also the initialized value). Setting 11362306a36Sopenharmony_ci * the T-bit resets the exception_index to zero. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Check for reserved-op exception. A reserved-op exception does not 11862306a36Sopenharmony_ci * set any exception registers nor does it set the T-bit. If the T-bit 11962306a36Sopenharmony_ci * is not set then a reserved-op exception occurred. 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * At some point, we may want to report reserved op exceptions as 12262306a36Sopenharmony_ci * illegal instructions. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!Is_tbit_set()) { 12662306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 12762306a36Sopenharmony_ci return SIGNALCODE(SIGILL, ILL_COPROC); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Is a coprocessor op. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Now we need to determine what type of exception occurred. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) { 13662306a36Sopenharmony_ci current_ir = Excp_instr(exception_index); 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * On PA89: there are 5 different unimplemented exception 13962306a36Sopenharmony_ci * codes: 0x1, 0x9, 0xb, 0x3, and 0x23. PA-RISC 2.0 adds 14062306a36Sopenharmony_ci * another, 0x2b. Only these have the low order bit set. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci excptype = Excp_type(exception_index); 14362306a36Sopenharmony_ci if (excptype & UNIMPLEMENTEDEXCEPTION) { 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Clear T-bit and exception register so that 14662306a36Sopenharmony_ci * we can tell if a trap really occurs while 14762306a36Sopenharmony_ci * emulating the instruction. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci Clear_tbit(); 15062306a36Sopenharmony_ci Clear_excp_register(exception_index); 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Now emulate this instruction. If a trap occurs, 15362306a36Sopenharmony_ci * fpudispatch will return a non-zero number 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci excp = fpudispatch(current_ir,excptype,0,Fpu_register); 15662306a36Sopenharmony_ci /* accumulate the status flags, don't lose them as in hpux */ 15762306a36Sopenharmony_ci if (excp) { 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * We now need to make sure that the T-bit and the 16062306a36Sopenharmony_ci * exception register contain the correct values 16162306a36Sopenharmony_ci * before continuing. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Set t-bit since it might still be needed for a 16562306a36Sopenharmony_ci * subsequent real trap (I don't understand fully -PB) 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci Set_tbit(); 16862306a36Sopenharmony_ci /* some of the following code uses 16962306a36Sopenharmony_ci * Excp_type(exception_index) so fix that up */ 17062306a36Sopenharmony_ci Set_exceptiontype_and_instr_field(excp,current_ir, 17162306a36Sopenharmony_ci Fpu_register[exception_index]); 17262306a36Sopenharmony_ci if (excp == UNIMPLEMENTEDEXCEPTION) { 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * it is really unimplemented, so restore the 17562306a36Sopenharmony_ci * TIMEX extended unimplemented exception code 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci excp = excptype; 17862306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 17962306a36Sopenharmony_ci trap_counts); 18062306a36Sopenharmony_ci return SIGNALCODE(SIGILL, ILL_COPROC); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci /* some of the following code uses excptype, so 18362306a36Sopenharmony_ci * fix that up too */ 18462306a36Sopenharmony_ci excptype = excp; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci /* handle exceptions other than the real UNIMPLIMENTED the 18762306a36Sopenharmony_ci * same way as if the hardware had caused them */ 18862306a36Sopenharmony_ci if (excp == NOEXCEPTION) 18962306a36Sopenharmony_ci /* For now use 'break', should technically be 'continue' */ 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * In PA89, the underflow exception has been extended to encode 19562306a36Sopenharmony_ci * additional information. The exception looks like pp01x0, 19662306a36Sopenharmony_ci * where x is 1 if inexact and pp represent the inexact bit (I) 19762306a36Sopenharmony_ci * and the round away bit (RA) 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci if (excptype & UNDERFLOWEXCEPTION) { 20062306a36Sopenharmony_ci /* check for underflow trap enabled */ 20162306a36Sopenharmony_ci if (Is_underflowtrap_enabled()) { 20262306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 20362306a36Sopenharmony_ci trap_counts); 20462306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTUND); 20562306a36Sopenharmony_ci } else { 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Isn't a real trap; we need to 20862306a36Sopenharmony_ci * return the default value. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci target = current_ir & fivebits; 21162306a36Sopenharmony_ci#ifndef lint 21262306a36Sopenharmony_ci if (Ibit(Fpu_register[exception_index])) inexact = TRUE; 21362306a36Sopenharmony_ci else inexact = FALSE; 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci switch (Excp_format()) { 21662306a36Sopenharmony_ci case SGL: 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * If ra (round-away) is set, will 21962306a36Sopenharmony_ci * want to undo the rounding done 22062306a36Sopenharmony_ci * by the hardware. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci if (Rabit(Fpu_register[exception_index])) 22362306a36Sopenharmony_ci Sgl_decrement(Fpu_sgl(target)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* now denormalize */ 22662306a36Sopenharmony_ci sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode()); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case DBL: 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * If ra (round-away) is set, will 23162306a36Sopenharmony_ci * want to undo the rounding done 23262306a36Sopenharmony_ci * by the hardware. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci if (Rabit(Fpu_register[exception_index])) 23562306a36Sopenharmony_ci Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target)); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* now denormalize */ 23862306a36Sopenharmony_ci dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target), 23962306a36Sopenharmony_ci &inexact,Rounding_mode()); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci if (inexact) Set_underflowflag(); 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * Underflow can generate an inexact 24562306a36Sopenharmony_ci * exception. If inexact trap is enabled, 24662306a36Sopenharmony_ci * want to do an inexact trap, otherwise 24762306a36Sopenharmony_ci * set inexact flag. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (inexact && Is_inexacttrap_enabled()) { 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * Set exception field of exception register 25262306a36Sopenharmony_ci * to inexact, parm field to zero. 25362306a36Sopenharmony_ci * Underflow bit should be cleared. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci Set_exceptiontype(Fpu_register[exception_index], 25662306a36Sopenharmony_ci INEXACTEXCEPTION); 25762306a36Sopenharmony_ci Set_parmfield(Fpu_register[exception_index],0); 25862306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 25962306a36Sopenharmony_ci trap_counts); 26062306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTRES); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci else { 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Exception register needs to be cleared. 26562306a36Sopenharmony_ci * Inexact flag needs to be set if inexact. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci Clear_excp_register(exception_index); 26862306a36Sopenharmony_ci if (inexact) Set_inexactflag(); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci continue; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci switch(Excp_type(exception_index)) { 27462306a36Sopenharmony_ci case OVERFLOWEXCEPTION: 27562306a36Sopenharmony_ci case OVERFLOWEXCEPTION | INEXACTEXCEPTION: 27662306a36Sopenharmony_ci /* check for overflow trap enabled */ 27762306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 27862306a36Sopenharmony_ci trap_counts); 27962306a36Sopenharmony_ci if (Is_overflowtrap_enabled()) { 28062306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 28162306a36Sopenharmony_ci trap_counts); 28262306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTOVF); 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Isn't a real trap; we need to 28662306a36Sopenharmony_ci * return the default value. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci target = current_ir & fivebits; 28962306a36Sopenharmony_ci switch (Excp_format()) { 29062306a36Sopenharmony_ci case SGL: 29162306a36Sopenharmony_ci Sgl_setoverflow(Fpu_sgl(target)); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case DBL: 29462306a36Sopenharmony_ci Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target)); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci Set_overflowflag(); 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * Overflow always generates an inexact 30062306a36Sopenharmony_ci * exception. If inexact trap is enabled, 30162306a36Sopenharmony_ci * want to do an inexact trap, otherwise 30262306a36Sopenharmony_ci * set inexact flag. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci if (Is_inexacttrap_enabled()) { 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * Set exception field of exception 30762306a36Sopenharmony_ci * register to inexact. Overflow 30862306a36Sopenharmony_ci * bit should be cleared. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci Set_exceptiontype(Fpu_register[exception_index], 31162306a36Sopenharmony_ci INEXACTEXCEPTION); 31262306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, 31362306a36Sopenharmony_ci trap_counts); 31462306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTRES); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci else { 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * Exception register needs to be cleared. 31962306a36Sopenharmony_ci * Inexact flag needs to be set. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci Clear_excp_register(exception_index); 32262306a36Sopenharmony_ci Set_inexactflag(); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case INVALIDEXCEPTION: 32762306a36Sopenharmony_ci case OPC_2E_INVALIDEXCEPTION: 32862306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 32962306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTINV); 33062306a36Sopenharmony_ci case DIVISIONBYZEROEXCEPTION: 33162306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 33262306a36Sopenharmony_ci Clear_excp_register(exception_index); 33362306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTDIV); 33462306a36Sopenharmony_ci case INEXACTEXCEPTION: 33562306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 33662306a36Sopenharmony_ci return SIGNALCODE(SIGFPE, FPE_FLTRES); 33762306a36Sopenharmony_ci default: 33862306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 33962306a36Sopenharmony_ci printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__, 34062306a36Sopenharmony_ci __LINE__, Excp_type(exception_index)); 34162306a36Sopenharmony_ci return SIGNALCODE(SIGILL, ILL_COPROC); 34262306a36Sopenharmony_ci case NOEXCEPTION: /* no exception */ 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * Clear exception register in case 34562306a36Sopenharmony_ci * other fields are non-zero. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci Clear_excp_register(exception_index); 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * No real exceptions occurred. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci Clear_tbit(); 35562306a36Sopenharmony_ci update_trap_counts(Fpu_register, aflags, bflags, trap_counts); 35662306a36Sopenharmony_ci return(NOTRAP); 35762306a36Sopenharmony_ci} 358