1 /*
2  * Linux/PA-RISC Project (http://www.parisc-linux.org/)
3  *
4  * Floating-point emulation code
5  *  Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org>
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation; either version 2, or (at your option)
10  *    any later version.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with this program; if not, write to the Free Software
19  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 /*
22  * BEGIN_DESC
23  *
24  *  File:
25  *	@(#)	pa/fp/decode_exc.c		$ Revision: $
26  *
27  *  Purpose:
28  *	<<please update with a synopsis of the functionality provided by this file>>
29  *
30  *  External Interfaces:
31  *	<<the following list was autogenerated, please review>>
32  *	decode_fpu(Fpu_register, trap_counts)
33  *
34  *  Internal Interfaces:
35  *	<<please update>>
36  *
37  *  Theory:
38  *	<<please update with a overview of the operation of this file>>
39  *
40  * END_DESC
41 */
42 
43 #include <linux/kernel.h>
44 #include "float.h"
45 #include "sgl_float.h"
46 #include "dbl_float.h"
47 #include "cnv_float.h"
48 /* #include "types.h" */
49 #include <asm/signal.h>
50 #include <asm/siginfo.h>
51 /* #include <machine/sys/mdep_private.h> */
52 
53 #undef Fpustatus_register
54 #define Fpustatus_register Fpu_register[0]
55 
56 /* General definitions */
57 #define DOESTRAP 1
58 #define NOTRAP 0
59 #define SIGNALCODE(signal, code) ((signal) << 24 | (code))
60 #define copropbit	1<<31-2	/* bit position 2 */
61 #define opclass		9	/* bits 21 & 22 */
62 #define fmt		11	/* bits 19 & 20 */
63 #define df		13	/* bits 17 & 18 */
64 #define twobits		3	/* mask low-order 2 bits */
65 #define fivebits	31	/* mask low-order 5 bits */
66 #define MAX_EXCP_REG	7	/* number of excpeption registers to check */
67 
68 /* Exception register definitions */
69 #define Excp_type(index) Exceptiontype(Fpu_register[index])
70 #define Excp_instr(index) Instructionfield(Fpu_register[index])
71 #define Clear_excp_register(index) Allexception(Fpu_register[index]) = 0
72 #define Excp_format() \
73     (current_ir >> ((current_ir>>opclass & twobits)==1 ? df : fmt) & twobits)
74 
75 /* Miscellaneous definitions */
76 #define Fpu_sgl(index) Fpu_register[index*2]
77 
78 #define Fpu_dblp1(index) Fpu_register[index*2]
79 #define Fpu_dblp2(index) Fpu_register[(index*2)+1]
80 
81 #define Fpu_quadp1(index) Fpu_register[index*2]
82 #define Fpu_quadp2(index) Fpu_register[(index*2)+1]
83 #define Fpu_quadp3(index) Fpu_register[(index*2)+2]
84 #define Fpu_quadp4(index) Fpu_register[(index*2)+3]
85 
86 /* Single precision floating-point definitions */
87 #ifndef Sgl_decrement
88 # define Sgl_decrement(sgl_value) Sall(sgl_value)--
89 #endif
90 
91 /* Double precision floating-point definitions */
92 #ifndef Dbl_decrement
93 # define Dbl_decrement(dbl_valuep1,dbl_valuep2) \
94     if ((Dallp2(dbl_valuep2)--) == 0) Dallp1(dbl_valuep1)--
95 #endif
96 
97 
98 #define update_trap_counts(Fpu_register, aflags, bflags, trap_counts) {	\
99 	aflags=(Fpu_register[0])>>27;	/* assumes zero fill. 32 bit */	\
100 	Fpu_register[0] |= bflags;					\
101 }
102 
103 u_int
decode_fpu(unsigned int Fpu_register[],unsigned int trap_counts[])104 decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[])
105 {
106     unsigned int current_ir, excp;
107     int target, exception_index = 1;
108     boolean inexact;
109     unsigned int aflags;
110     unsigned int bflags;
111     unsigned int excptype;
112 
113 
114     /* Keep stats on how many floating point exceptions (based on type)
115      * that happen.  Want to keep this overhead low, but still provide
116      * some information to the customer.  All exits from this routine
117      * need to restore Fpu_register[0]
118     */
119 
120     bflags=(Fpu_register[0] & 0xf8000000);
121     Fpu_register[0] &= 0x07ffffff;
122 
123     /* exception_index is used to index the exception register queue.  It
124      *   always points at the last register that contains a valid exception.  A
125      *   zero value implies no exceptions (also the initialized value).  Setting
126      *   the T-bit resets the exception_index to zero.
127      */
128 
129     /*
130      * Check for reserved-op exception.  A reserved-op exception does not
131      * set any exception registers nor does it set the T-bit.  If the T-bit
132      * is not set then a reserved-op exception occurred.
133      *
134      * At some point, we may want to report reserved op exceptions as
135      * illegal instructions.
136      */
137 
138     if (!Is_tbit_set()) {
139 	update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
140 	return SIGNALCODE(SIGILL, ILL_COPROC);
141     }
142 
143     /*
144      * Is a coprocessor op.
145      *
146      * Now we need to determine what type of exception occurred.
147      */
148     for (exception_index=1; exception_index<=MAX_EXCP_REG; exception_index++) {
149 	current_ir = Excp_instr(exception_index);
150 	  /*
151 	   * On PA89: there are 5 different unimplemented exception
152 	   * codes: 0x1, 0x9, 0xb, 0x3, and 0x23.  PA-RISC 2.0 adds
153 	   * another, 0x2b.  Only these have the low order bit set.
154 	   */
155 	excptype = Excp_type(exception_index);
156 	if (excptype & UNIMPLEMENTEDEXCEPTION) {
157 		/*
158 		 * Clear T-bit and exception register so that
159 		 * we can tell if a trap really occurs while
160 		 * emulating the instruction.
161 		 */
162 		Clear_tbit();
163 		Clear_excp_register(exception_index);
164 		/*
165 		 * Now emulate this instruction.  If a trap occurs,
166 		 * fpudispatch will return a non-zero number
167 		 */
168 		excp = fpudispatch(current_ir,excptype,0,Fpu_register);
169 		/* accumulate the status flags, don't lose them as in hpux */
170 		if (excp) {
171 			/*
172 			 * We now need to make sure that the T-bit and the
173 			 * exception register contain the correct values
174 			 * before continuing.
175 			 */
176 			/*
177 			 * Set t-bit since it might still be needed for a
178 			 * subsequent real trap (I don't understand fully -PB)
179 			 */
180 			Set_tbit();
181 			/* some of the following code uses
182 			 * Excp_type(exception_index) so fix that up */
183 			Set_exceptiontype_and_instr_field(excp,current_ir,
184 			 Fpu_register[exception_index]);
185 			if (excp == UNIMPLEMENTEDEXCEPTION) {
186 				/*
187 			 	 * it is really unimplemented, so restore the
188 			 	 * TIMEX extended unimplemented exception code
189 			 	 */
190 				excp = excptype;
191 				update_trap_counts(Fpu_register, aflags, bflags,
192 					   trap_counts);
193 				return SIGNALCODE(SIGILL, ILL_COPROC);
194 			}
195 			/* some of the following code uses excptype, so
196 			 * fix that up too */
197 			excptype = excp;
198 		}
199 		/* handle exceptions other than the real UNIMPLIMENTED the
200 		 * same way as if the hardware had caused them */
201 		if (excp == NOEXCEPTION)
202 			/* For now use 'break', should technically be 'continue' */
203 			break;
204 	}
205 
206 	  /*
207 	   * In PA89, the underflow exception has been extended to encode
208 	   * additional information.  The exception looks like pp01x0,
209 	   * where x is 1 if inexact and pp represent the inexact bit (I)
210 	   * and the round away bit (RA)
211 	   */
212 	if (excptype & UNDERFLOWEXCEPTION) {
213 		/* check for underflow trap enabled */
214 		if (Is_underflowtrap_enabled()) {
215 			update_trap_counts(Fpu_register, aflags, bflags,
216 					   trap_counts);
217 			return SIGNALCODE(SIGFPE, FPE_FLTUND);
218 		} else {
219 		    /*
220 		     * Isn't a real trap; we need to
221 		     * return the default value.
222 		     */
223 		    target = current_ir & fivebits;
224 #ifndef lint
225 		    if (Ibit(Fpu_register[exception_index])) inexact = TRUE;
226 		    else inexact = FALSE;
227 #endif
228 		    switch (Excp_format()) {
229 		      case SGL:
230 		        /*
231 		         * If ra (round-away) is set, will
232 		         * want to undo the rounding done
233 		         * by the hardware.
234 		         */
235 		        if (Rabit(Fpu_register[exception_index]))
236 				Sgl_decrement(Fpu_sgl(target));
237 
238 			/* now denormalize */
239 			sgl_denormalize(&Fpu_sgl(target),&inexact,Rounding_mode());
240 		    	break;
241 		      case DBL:
242 		    	/*
243 		    	 * If ra (round-away) is set, will
244 		    	 * want to undo the rounding done
245 		    	 * by the hardware.
246 		    	 */
247 		    	if (Rabit(Fpu_register[exception_index]))
248 				Dbl_decrement(Fpu_dblp1(target),Fpu_dblp2(target));
249 
250 			/* now denormalize */
251 			dbl_denormalize(&Fpu_dblp1(target),&Fpu_dblp2(target),
252 			  &inexact,Rounding_mode());
253 		    	break;
254 		    }
255 		    if (inexact) Set_underflowflag();
256 		    /*
257 		     * Underflow can generate an inexact
258 		     * exception.  If inexact trap is enabled,
259 		     * want to do an inexact trap, otherwise
260 		     * set inexact flag.
261 		     */
262 		    if (inexact && Is_inexacttrap_enabled()) {
263 		    	/*
264 		    	 * Set exception field of exception register
265 		    	 * to inexact, parm field to zero.
266 			 * Underflow bit should be cleared.
267 		    	 */
268 		    	Set_exceptiontype(Fpu_register[exception_index],
269 			 INEXACTEXCEPTION);
270 			Set_parmfield(Fpu_register[exception_index],0);
271 			update_trap_counts(Fpu_register, aflags, bflags,
272 					   trap_counts);
273 			return SIGNALCODE(SIGFPE, FPE_FLTRES);
274 		    }
275 		    else {
276 		    	/*
277 		    	 * Exception register needs to be cleared.
278 			 * Inexact flag needs to be set if inexact.
279 		    	 */
280 		    	Clear_excp_register(exception_index);
281 		    	if (inexact) Set_inexactflag();
282 		    }
283 		}
284 		continue;
285 	}
286 	switch(Excp_type(exception_index)) {
287 	  case OVERFLOWEXCEPTION:
288 	  case OVERFLOWEXCEPTION | INEXACTEXCEPTION:
289 		/* check for overflow trap enabled */
290 			update_trap_counts(Fpu_register, aflags, bflags,
291 					   trap_counts);
292 		if (Is_overflowtrap_enabled()) {
293 			update_trap_counts(Fpu_register, aflags, bflags,
294 					   trap_counts);
295 			return SIGNALCODE(SIGFPE, FPE_FLTOVF);
296 		} else {
297 			/*
298 			 * Isn't a real trap; we need to
299 			 * return the default value.
300 			 */
301 			target = current_ir & fivebits;
302 			switch (Excp_format()) {
303 			  case SGL:
304 				Sgl_setoverflow(Fpu_sgl(target));
305 				break;
306 			  case DBL:
307 				Dbl_setoverflow(Fpu_dblp1(target),Fpu_dblp2(target));
308 				break;
309 			}
310 			Set_overflowflag();
311 			/*
312 			 * Overflow always generates an inexact
313 			 * exception.  If inexact trap is enabled,
314 			 * want to do an inexact trap, otherwise
315 			 * set inexact flag.
316 			 */
317 			if (Is_inexacttrap_enabled()) {
318 				/*
319 				 * Set exception field of exception
320 				 * register to inexact.  Overflow
321 				 * bit should be cleared.
322 				 */
323 				Set_exceptiontype(Fpu_register[exception_index],
324 				 INEXACTEXCEPTION);
325 				update_trap_counts(Fpu_register, aflags, bflags,
326 					   trap_counts);
327 				return SIGNALCODE(SIGFPE, FPE_FLTRES);
328 			}
329 			else {
330 				/*
331 				 * Exception register needs to be cleared.
332 				 * Inexact flag needs to be set.
333 				 */
334 				Clear_excp_register(exception_index);
335 				Set_inexactflag();
336 			}
337 		}
338 		break;
339 	  case INVALIDEXCEPTION:
340 	  case OPC_2E_INVALIDEXCEPTION:
341 		update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
342 		return SIGNALCODE(SIGFPE, FPE_FLTINV);
343 	  case DIVISIONBYZEROEXCEPTION:
344 		update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
345 		Clear_excp_register(exception_index);
346 	  	return SIGNALCODE(SIGFPE, FPE_FLTDIV);
347 	  case INEXACTEXCEPTION:
348 		update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
349 		return SIGNALCODE(SIGFPE, FPE_FLTRES);
350 	  default:
351 		update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
352 		printk("%s(%d) Unknown FPU exception 0x%x\n", __FILE__,
353 			__LINE__, Excp_type(exception_index));
354 		return SIGNALCODE(SIGILL, ILL_COPROC);
355 	  case NOEXCEPTION:	/* no exception */
356 		/*
357 		 * Clear exception register in case
358 		 * other fields are non-zero.
359 		 */
360 		Clear_excp_register(exception_index);
361 		break;
362 	}
363     }
364     /*
365      * No real exceptions occurred.
366      */
367     Clear_tbit();
368     update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
369     return(NOTRAP);
370 }
371