130 строки
3.8 KiB
C
130 строки
3.8 KiB
C
/*
|
|
* 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/signal.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;
|
|
}
|