aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc/math-emu/driver.c
blob: 0d10efb53361204744793d41b9c08ec8eb768b46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * 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];

	clear_siginfo(&si);
	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;
}