| /* | 
 |  * 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 | 
 |  */ | 
 | /* | 
 |  *  linux/arch/math-emu/driver.c.c | 
 |  * | 
 |  *	decodes and dispatches unimplemented FPU instructions | 
 |  * | 
 |  *  Copyright (C) 1999, 2000  Philipp Rumpf <prumpf@tux.org> | 
 |  *  Copyright (C) 2001	      Hewlett-Packard <bame@debian.org> | 
 |  */ | 
 |  | 
 | #include <linux/sched.h> | 
 | #include "float.h" | 
 | #include "math-emu.h" | 
 |  | 
 |  | 
 | #define fptpos 31 | 
 | #define fpr1pos 10 | 
 | #define extru(r,pos,len) (((r) >> (31-(pos))) & (( 1 << (len)) - 1)) | 
 |  | 
 | #define FPUDEBUG 0 | 
 |  | 
 | /* Format of the floating-point exception registers. */ | 
 | struct exc_reg { | 
 | 	unsigned int exception : 6; | 
 | 	unsigned int ei : 26; | 
 | }; | 
 |  | 
 | /* Macros for grabbing bits of the instruction format from the 'ei' | 
 |    field above. */ | 
 | /* Major opcode 0c and 0e */ | 
 | #define FP0CE_UID(i) (((i) >> 6) & 3) | 
 | #define FP0CE_CLASS(i) (((i) >> 9) & 3) | 
 | #define FP0CE_SUBOP(i) (((i) >> 13) & 7) | 
 | #define FP0CE_SUBOP1(i) (((i) >> 15) & 7) /* Class 1 subopcode */ | 
 | #define FP0C_FORMAT(i) (((i) >> 11) & 3) | 
 | #define FP0E_FORMAT(i) (((i) >> 11) & 1) | 
 |  | 
 | /* Major opcode 0c, uid 2 (performance monitoring) */ | 
 | #define FPPM_SUBOP(i) (((i) >> 9) & 0x1f) | 
 |  | 
 | /* Major opcode 2e (fused operations).   */ | 
 | #define FP2E_SUBOP(i)  (((i) >> 5) & 1) | 
 | #define FP2E_FORMAT(i) (((i) >> 11) & 1) | 
 |  | 
 | /* Major opcode 26 (FMPYSUB) */ | 
 | /* Major opcode 06 (FMPYADD) */ | 
 | #define FPx6_FORMAT(i) ((i) & 0x1f) | 
 |  | 
 | /* Flags and enable bits of the status word. */ | 
 | #define FPSW_FLAGS(w) ((w) >> 27) | 
 | #define FPSW_ENABLE(w) ((w) & 0x1f) | 
 | #define FPSW_V (1<<4) | 
 | #define FPSW_Z (1<<3) | 
 | #define FPSW_O (1<<2) | 
 | #define FPSW_U (1<<1) | 
 | #define FPSW_I (1<<0) | 
 |  | 
 | /* Handle a floating point exception.  Return zero if the faulting | 
 |    instruction can be completed successfully. */ | 
 | int | 
 | handle_fpe(struct pt_regs *regs) | 
 | { | 
 | 	extern void printbinary(unsigned long x, int nbits); | 
 | 	struct siginfo si; | 
 | 	unsigned int orig_sw, sw; | 
 | 	int signalcode; | 
 | 	/* need an intermediate copy of float regs because FPU emulation | 
 | 	 * code expects an artificial last entry which contains zero | 
 | 	 * | 
 | 	 * also, the passed in fr registers contain one word that defines | 
 | 	 * the fpu type. the fpu type information is constructed  | 
 | 	 * inside the emulation code | 
 | 	 */ | 
 | 	__u64 frcopy[36]; | 
 |  | 
 | 	memcpy(frcopy, regs->fr, sizeof regs->fr); | 
 | 	frcopy[32] = 0; | 
 |  | 
 | 	memcpy(&orig_sw, frcopy, sizeof(orig_sw)); | 
 |  | 
 | 	if (FPUDEBUG) { | 
 | 		printk(KERN_DEBUG "FP VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI ->\n   "); | 
 | 		printbinary(orig_sw, 32); | 
 | 		printk(KERN_DEBUG "\n"); | 
 | 	} | 
 |  | 
 | 	signalcode = decode_fpu(frcopy, 0x666); | 
 |  | 
 | 	/* Status word = FR0L. */ | 
 | 	memcpy(&sw, frcopy, sizeof(sw)); | 
 | 	if (FPUDEBUG) { | 
 | 		printk(KERN_DEBUG "VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI decode_fpu returns %d|0x%x\n", | 
 | 			signalcode >> 24, signalcode & 0xffffff); | 
 | 		printbinary(sw, 32); | 
 | 		printk(KERN_DEBUG "\n"); | 
 | 	} | 
 |  | 
 | 	memcpy(regs->fr, frcopy, sizeof regs->fr); | 
 | 	if (signalcode != 0) { | 
 | 	    si.si_signo = signalcode >> 24; | 
 | 	    si.si_errno = 0; | 
 | 	    si.si_code = signalcode & 0xffffff; | 
 | 	    si.si_addr = (void __user *) regs->iaoq[0]; | 
 | 	    force_sig_info(si.si_signo, &si, current); | 
 | 	    return -1; | 
 | 	} | 
 |  | 
 | 	return signalcode ? -1 : 0; | 
 | } |