[parisc-linux-cvs] FPU emulation changes
Paul Bame
bame@fc.hp.com
Fri, 16 Mar 2001 16:01:25 -0700
I'm not including the code copied from HP-UX in this diff, just the
more linux-related code. You can read the other code in arch/parisc/math-emu
Index: arch/parisc/Makefile
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/Makefile,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -u -r1.23 -r1.24
--- Makefile 2001/03/02 00:48:47 1.23
+++ Makefile 2001/03/16 22:51:08 1.24
@@ -76,10 +76,8 @@
LIBS := `$(CC) -print-libgcc-file-name` $(TOPDIR)/arch/parisc/lib/lib.a $(LIBS)
-ifdef CONFIG_MATH_EMULATION
SUBDIRS := $(SUBDIRS) arch/parisc/math-emu
-DRIVERS := $(DRIVERS) arch/parisc/math-emu/math.a
-endif
+DRIVERS := $(DRIVERS) arch/parisc/math-emu/math.o
ifdef CONFIG_KWDB
SUBDIRS := $(SUBDIRS) arch/parisc/kdb
Index: arch/parisc/kernel/traps.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/traps.c,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -u -r1.44 -r1.45
--- traps.c 2001/03/13 20:26:37 1.44
+++ traps.c 2001/03/16 22:51:09 1.45
@@ -32,6 +32,8 @@
#include <asm/smp.h>
#include <asm/pdc.h>
+#include "../math-emu/math-emu.h" /* for handle_fpe() */
+
#ifdef CONFIG_KWDB
#include <kdb/break.h> /* for BI2_KGDB_GDB */
#include <kdb/kgdb_types.h> /* for __() */
@@ -218,220 +220,6 @@
}
}
-/* 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)
-
-/* Emulate a floating point instruction if necessary and possible
- (this will be moved elsewhere eventually). Return zero if
- successful or if emulation was not required, -1 if the instruction
- is actually illegal or unimplemented. The status word passed as
- the first parameter will be modified to signal exceptions, if
- any. */
-
-/* FIXME!!! This is really incomplete and, at the moment, most
- illegal FP instructions will simply act as no-ops. Obviously that
- is *not* what we want. Also we don't even try to handle exception
- types other than the 'unimplemented' ones. */
-int
-fp_emul_insn(u32 *sw, struct exc_reg exc, struct pt_regs *regs)
-{
- switch (exc.exception) {
- case 0x3: /* Unimplemented, opcode 06 */
- break;
- case 0x9: /* Unimplemented, opcode 0c */
- /* We do not support quadword operations, end of
- story. There's no support for them in GCC. */
- if (FP0C_FORMAT(exc.ei) == 3)
- return -1; /* SIGILL */
- /* Fall through. */
- case 0xa: /* Unimplemented, opcode 0e */
- if (FP0CE_CLASS(exc.ei) == 1) {
- /* FCNV instructions of various sorts. */
- } else {
- if (FP0CE_CLASS(exc.ei == 0)
- && FP0CE_SUBOP(exc.ei == 5)) {
- /* FRND instructions should be
- emulated, at some point, I
- guess. */
- return -1; /* SIGILL */
- }
- }
- break;
- case 0x23: /* Unimplemented, opcode 26 */
- break;
- case 0x2b: /* Unimplemented, opcode 2e */
- break;
- case 0x1: /* Unimplemented, opcode 0e/0c */
- /* FIXME: How the hell are we supposed to tell which
- opcode it is? */
- break;
- default:
- return -1; /* Punt */
- }
-
- printk("FP emulation: exception 0x%03lx:%06lx\n", exc.exception, exc.ei);
- return 0;
-}
-
-/* Handle a floating point exception. Return zero if the faulting
- instruction can be completed successfully. */
-int
-handle_fpe(struct pt_regs *regs)
-{
- struct siginfo si;
- union {
- struct fpsw {
- /* flag bits */
- unsigned int fv : 1;
- unsigned int fz : 1;
- unsigned int fo : 1;
- unsigned int fu : 1;
- unsigned int fi : 1;
-
- unsigned int c : 1;
- unsigned int pad1 : 4;
- unsigned int cq : 11;
- unsigned int rm : 2;
- unsigned int pad2 : 2;
- unsigned int t : 1;
- unsigned int d : 1;
-
- /* enable bits */
- unsigned int ev : 1;
- unsigned int ez : 1;
- unsigned int eo : 1;
- unsigned int eu : 1;
- unsigned int ei : 1;
- } status;
- u32 word;
- } sw;
- struct exc_reg excepts[7];
- unsigned int code = 0;
- unsigned int throw;
-
- /* Status word = FR0L. */
- memcpy(&sw, regs->fr, sizeof(sw));
- /* Exception words = FR0R-FR3R. */
- memcpy(excepts, ((char *) regs->fr) + 4, sizeof(excepts));
-
- /* This is all CPU dependent. Since there is no public
- documentation on the PA2.0 processors we will just assume
- everything is like the 7100/7100LC/7300LC for now.
-
- Specifically: All exceptions are marked as "unimplemented"
- in the exception word, and the only exception word used is
- excepts[1]. */
-
- /* Try to emulate the instruction. Also determine if it is
- really an illegal instruction in the process.
-
- FIXME: fp_emul_insn() only checks for the "unimplemented"
- exceptions at the moment. So this may break horribly on
- PA2.0, where we may want to also check to see if we should
- just send SIGFPE (or maybe not, let's see the documentation
- first...) */
- if (fp_emul_insn(&sw.word, excepts[1], regs) == -1)
- goto send_sigill;
-
- /* Take the intersection of the flag bits in the FPSW and the
- enable bits in the FPSW. */
- throw = FPSW_FLAGS(sw.word) & FPSW_ENABLE(sw.word);
-
- /* Concoct an appropriate si_code. Of course we don't know
- what to do if multiple exceptions were enabled and multiple
- flags were set. Maybe that's why HP/UX doesn't implement
- feenableexcept(). */
-
- if (throw == 0)
- goto success; /* Duh. */
- else if (throw & FPSW_V)
- code = FPE_FLTINV;
- else if (throw & FPSW_Z)
- code = FPE_FLTDIV;
- else if (throw & FPSW_O)
- code = FPE_FLTOVF;
- else if (throw & FPSW_U)
- code = FPE_FLTUND;
- else if (throw & FPSW_I)
- code = FPE_FLTRES;
-
-#if 1 /* Debugging... */
- printk("Unemulated floating point exception, pid=%d (%s)\n",
- current->pid, current->comm);
- show_regs(regs);
- {
- int i;
- printk("FP Status: %08x\n", sw.word);
- printk("FP Exceptions:\n");
- for (i = 0; i < 7; i++) {
- printk("\tExcept%d: exception %03x insn %06x\n",
- i, excepts[i].exception, excepts[i].ei);
- }
- }
-#endif
-
- /* FIXME: Should we clear the flag bits, T bit, and exception
- registers here? */
-
- si.si_signo = SIGFPE;
- si.si_errno = 0;
- si.si_code = code;
- si.si_addr = (void *) regs->iaoq[0];
- force_sig_info(SIGFPE, &si, current);
- return -1;
-
- send_sigill:
- si.si_signo = SIGILL;
- si.si_errno = 0;
- si.si_code = ILL_COPROC;
- si.si_addr = (void *) regs->iaoq[0];
- force_sig_info(SIGILL, &si, current);
- return -1;
-
- success:
- /* We absolutely have to clear the T bit and exception
- registers to allow the process to recover. Otherwise every
- subsequent floating point instruction will trap. */
- sw.status.t = 0;
- memset(excepts, 0, sizeof(excepts));
-
- memcpy(regs->fr, &sw, sizeof(sw));
- memcpy(((char *) regs->fr) + 4,excepts , sizeof(excepts));
- return 0;
-}
int handle_toc(void)
{
Index: arch/parisc/math-emu/Makefile
===================================================================
RCS file: Makefile
diff -N Makefile
--- /dev/null Tue May 5 14:32:27 1998
+++ /tmp/cvs09130eaa Fri Mar 16 15:54:50 2001
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux parisc-specific parts of the memory manager.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := math.o
+obj-y := frnd.o driver.o
+
+ifdef CONFIG_MATH_EMULATION
+# Math emulation code beyond the FRND is required for 712/80i and
+# other very old or stripped-down PA-RISC CPUs -- not currently supported
+obj-y += unimplemented-math-emulation.o
+endif
+
+include $(TOPDIR)/Rules.make
Index: arch/parisc/math-emu/README
===================================================================
RCS file: README
diff -N README
--- /dev/null Tue May 5 14:32:27 1998
+++ /tmp/cvs09130faa Fri Mar 16 15:54:50 2001
@@ -0,0 +1,11 @@
+All files except driver.c are snapshots from the HP-UX kernel. They've
+been modified as little as possible. Even though they don't fit the
+Linux coding style, please leave them in their funny format just in case
+someone in the future, with access to HP-UX source code, is generous
+enough to update our copies with later changes from HP-UX -- it'll
+make their 'diff' job easier if our code is relatively unmodified.
+
+Required Disclaimer: Hewlett-Packard makes no implied or expressed
+warranties about this code nor any promises to maintain or test it
+in any way. This copy of this snapshot is no longer the property
+of Hewlett-Packard.
Index: arch/parisc/math-emu/driver.c
===================================================================
RCS file: driver.c
diff -N driver.c
--- /dev/null Tue May 5 14:32:27 1998
+++ /tmp/cvs09130gaa Fri Mar 16 15:54:50 2001
@@ -0,0 +1,335 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, 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/config.h>
+#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))
+
+/* 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)
+
+/* return -1 if unemulatable otherwise 0 */
+static int frnd(u32 *sw, struct exc_reg exc, struct pt_regs *regs)
+{
+ int r = 0;
+ int srcreg, destreg;
+ __u64 *srcp, *destp;
+ __u64 fpzeroreg = 0;
+ extern int sgl_frnd(sgl_floating_point *srcptr,
+ unsigned int *nullptr,
+ sgl_floating_point *dstptr,
+ unsigned int *status);
+ extern int dbl_frnd(dbl_floating_point *srcptr,
+ unsigned int *nullptr,
+ dbl_floating_point *dstptr,
+ unsigned int *status);
+
+ /* source register */
+ srcreg = extru(exc.ei,fpr1pos,5);
+ if (srcreg == 0) /* map fr0 source to constant zero */
+ srcp = &fpzeroreg;
+ else
+ srcp = ®s->fr[srcreg];
+
+ destreg = extru(exc.ei,fptpos,5);
+ if (destreg == 0) /* fr0 as destination -- a NO-OP */
+ return 0;
+
+ destp = ®s->fr[destreg];
+
+ switch(FP0C_FORMAT(exc.ei)) {
+ case 0: /* single-precision */
+ sgl_frnd((sgl_floating_point *)srcp, 0,
+ (sgl_floating_point *)destp, sw);
+ break;
+ case 1: /* double-precision */
+ dbl_frnd((dbl_floating_point *)srcp, 0,
+ (dbl_floating_point *)destp, sw);
+ break;
+ default: /* quad precision unimplemented */
+ r = -1;
+ break;
+ }
+
+ return r;
+}
+
+/* Decode the floating-point exception and if it's from an unimplemented
+ * FRND instruction, emulate it. Return 0 if the instruction was successfully
+ * emulated, or if the exception code indicates the floating-point status
+ * register is sufficient to enable the caller, handle_fpe(), to do the
+ * right thing. Return -1 to signal an illegal or unemulatable instruction
+ * or a "reserved" exception code value. May modify the status word.
+ */
+static int
+fp_emul_insn(u32 *sw, struct exc_reg exc, struct pt_regs *regs)
+{
+ int r = -1;
+
+#if 0 /* Debug */
+ printk("@@ FP emulation: exception 0x%02x:%06x fmt %d class %d subop %d\n",
+ exc.exception,
+ exc.ei,
+ FP0C_FORMAT(exc.ei),
+ FP0CE_CLASS(exc.ei),
+ FP0CE_SUBOP(exc.ei)
+ );
+ show_regs(regs);
+#endif
+
+ switch (exc.exception & 0x0f) {
+ case 0x04: /* Underflow */
+ case 0x06: /* Inexact & Underflow */
+ r = 0;
+ break;
+ default:
+ switch (exc.exception) {
+ case 0x01: /* Unimplemented, opcode 0C/0E */
+ case 0x09: /* Unimplemented, opcode 0C */
+ case 0x0b: /* Unimplemented, opcode 0E */
+ /* All we need to decode is FRND instructions. */
+ if (FP0CE_CLASS(exc.ei) == 0 && FP0CE_SUBOP(exc.ei) == 5) {
+ r = frnd(sw, exc, regs);
+ }
+ break;
+ /* can't emulate any of these (yet) */
+ case 0x03: /* Unimplemented, opcode 06 */
+ case 0x23: /* Unimplemented, opcode 26 */
+ case 0x2b: /* Unimplemented, opcode 2e */
+ break;
+ /* non-unimplemented exception codes. Maybe these should be
+ * handled elsewhere. For now, return "success" and let the
+ * fp status decode handle returning the proper signal. This
+ * is also how the "Underflow" cases in the outer switch work.
+ */
+ case 0x00: /* No exception, probably a bug */
+ BUG();
+ case 0x20: /* Invalid operation */
+ case 0x10: /* Divide by zero */
+ case 0x08: /* Overflow */
+ case 0x02: /* Inexact */
+ case 0x0a: /* Inexact & Overflow */
+ r = 0;
+ break;
+ default: /* Reserved exception values */
+ break;
+ }
+ }
+
+ if (r == -1)
+ {
+ printk("%d(%s): Unemulated floating instruction: exception 0x%02x:%06x fmt %d class %d subop %d\n",
+ current->pid,
+ current->comm,
+ exc.exception,
+ exc.ei,
+ FP0C_FORMAT(exc.ei),
+ FP0CE_CLASS(exc.ei),
+ FP0CE_SUBOP(exc.ei)
+ );
+ show_regs(regs);
+ }
+
+ return 0;
+}
+
+/* Handle a floating point exception. Return zero if the faulting
+ instruction can be completed successfully. */
+int
+handle_fpe(struct pt_regs *regs)
+{
+ struct siginfo si;
+ int i, ereg;
+ union {
+ struct fpsw {
+ /* flag bits */
+ unsigned int fv : 1;
+ unsigned int fz : 1;
+ unsigned int fo : 1;
+ unsigned int fu : 1;
+ unsigned int fi : 1;
+
+ unsigned int c : 1;
+ unsigned int pad1 : 4;
+ unsigned int cq : 11;
+ unsigned int rm : 2;
+ unsigned int pad2 : 2;
+ unsigned int t : 1;
+ unsigned int d : 1;
+
+ /* enable bits */
+ unsigned int ev : 1;
+ unsigned int ez : 1;
+ unsigned int eo : 1;
+ unsigned int eu : 1;
+ unsigned int ei : 1;
+ } status;
+ u32 word;
+ } sw;
+ struct exc_reg excepts[7];
+ unsigned int code = 0;
+ unsigned int throw;
+
+ /* Status word = FR0L. */
+ memcpy(&sw, regs->fr, sizeof(sw));
+ /* Exception words = FR0R-FR3R. */
+ memcpy(excepts, ((char *) regs->fr) + 4, sizeof(excepts));
+
+ /* This is all CPU dependent. Since there is no public
+ documentation on the PA2.0 processors we will just assume
+ everything is like the 7100/7100LC/7300LC for now.
+
+ Specifically: All exceptions are marked as "unimplemented"
+ in the exception word, and the only exception word used is
+ excepts[1]. */
+
+ /* Try to emulate the instruction. Also determine if it is
+ really an illegal instruction in the process.
+
+ FIXME: fp_emul_insn() only checks for the "unimplemented"
+ exceptions at the moment. So this may break horribly on
+ PA2.0, where we may want to also check to see if we should
+ just send SIGFPE (or maybe not, let's see the documentation
+ first...) */
+
+ /* process the first-found exception */
+ for (ereg = 0; ereg < 7; ereg++) {
+ if (excepts[ereg].exception != 0) {
+ i = fp_emul_insn(&sw.word, excepts[ereg], regs);
+ if (i == -1)
+ goto send_sigill;
+ break;
+ }
+ }
+
+ /* Take the intersection of the flag bits in the FPSW and the
+ enable bits in the FPSW. */
+ throw = FPSW_FLAGS(sw.word) & FPSW_ENABLE(sw.word);
+
+ /* Concoct an appropriate si_code. Of course we don't know
+ what to do if multiple exceptions were enabled and multiple
+ flags were set. Maybe that's why HP/UX doesn't implement
+ feenableexcept(). */
+
+ if (throw == 0)
+ goto success; /* Duh. */
+ else if (throw & FPSW_V)
+ code = FPE_FLTINV;
+ else if (throw & FPSW_Z)
+ code = FPE_FLTDIV;
+ else if (throw & FPSW_O)
+ code = FPE_FLTOVF;
+ else if (throw & FPSW_U)
+ code = FPE_FLTUND;
+ else if (throw & FPSW_I)
+ code = FPE_FLTRES;
+
+#if 1 /* Debugging... */
+ printk("Unemulated floating point exception, pid=%d (%s)\n",
+ current->pid, current->comm);
+ show_regs(regs);
+ {
+ int i;
+ printk("FP Status: %08x\n", sw.word);
+ printk("FP Exceptions:\n");
+ for (i = 0; i < 7; i++) {
+ printk("\tExcept%d: exception %03x insn %06x\n",
+ i, excepts[i].exception, excepts[i].ei);
+ }
+ }
+#endif
+
+ /* FIXME: Should we clear the flag bits, T bit, and exception
+ registers here? */
+
+ si.si_signo = SIGFPE;
+ si.si_errno = 0;
+ si.si_code = code;
+ si.si_addr = (void *) regs->iaoq[0];
+ force_sig_info(SIGFPE, &si, current);
+ return -1;
+
+ send_sigill:
+ si.si_signo = SIGILL;
+ si.si_errno = 0;
+ si.si_code = ILL_COPROC;
+ si.si_addr = (void *) regs->iaoq[0];
+ force_sig_info(SIGILL, &si, current);
+ return -1;
+
+ success:
+ /* We absolutely have to clear the T bit and exception
+ registers to allow the process to recover. Otherwise every
+ subsequent floating point instruction will trap. */
+ sw.status.t = 0;
+ memset(excepts, 0, sizeof(excepts));
+
+ memcpy(regs->fr, &sw, sizeof(sw));
+ memcpy(((char *) regs->fr) + 4,excepts , sizeof(excepts));
+ return 0;
+}
Index: arch/parisc/math-emu/math-emu.h
===================================================================
RCS file: math-emu.h
diff -N math-emu.h
--- /dev/null Tue May 5 14:32:27 1998
+++ /tmp/cvs09130haa Fri Mar 16 15:54:50 2001
@@ -0,0 +1,7 @@
+#ifndef _PARISC_MATH_EMU_H
+#define _PARISC_MATH_EMU_H
+
+#include <asm/ptrace.h>
+extern int handle_fpe(struct pt_regs *regs);
+
+#endif