/* * Linux/PA-RISC Project (http://www.parisc-linux.org/) * * Floating-point emulation code * Copyright (C) 2001 Hewlett-Packard (Paul Bame) * * 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 * Copyright (C) 2001 Hewlett-Packard */ #include #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; }