1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Linux/PA-RISC Project (http://www.parisc-linux.org/) |
4 | * |
5 | * Floating-point emulation code |
6 | * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> |
7 | */ |
8 | /* |
9 | * linux/arch/math-emu/driver.c.c |
10 | * |
11 | * decodes and dispatches unimplemented FPU instructions |
12 | * |
13 | * Copyright (C) 1999, 2000 Philipp Rumpf <prumpf@tux.org> |
14 | * Copyright (C) 2001 Hewlett-Packard <bame@debian.org> |
15 | */ |
16 | |
17 | #include <linux/sched/signal.h> |
18 | |
19 | #include "float.h" |
20 | #include "math-emu.h" |
21 | |
22 | |
23 | #define fptpos 31 |
24 | #define fpr1pos 10 |
25 | #define extru(r,pos,len) (((r) >> (31-(pos))) & (( 1 << (len)) - 1)) |
26 | |
27 | #define FPUDEBUG 0 |
28 | |
29 | /* Format of the floating-point exception registers. */ |
30 | struct exc_reg { |
31 | unsigned int exception : 6; |
32 | unsigned int ei : 26; |
33 | }; |
34 | |
35 | /* Macros for grabbing bits of the instruction format from the 'ei' |
36 | field above. */ |
37 | /* Major opcode 0c and 0e */ |
38 | #define FP0CE_UID(i) (((i) >> 6) & 3) |
39 | #define FP0CE_CLASS(i) (((i) >> 9) & 3) |
40 | #define FP0CE_SUBOP(i) (((i) >> 13) & 7) |
41 | #define FP0CE_SUBOP1(i) (((i) >> 15) & 7) /* Class 1 subopcode */ |
42 | #define FP0C_FORMAT(i) (((i) >> 11) & 3) |
43 | #define FP0E_FORMAT(i) (((i) >> 11) & 1) |
44 | |
45 | /* Major opcode 0c, uid 2 (performance monitoring) */ |
46 | #define FPPM_SUBOP(i) (((i) >> 9) & 0x1f) |
47 | |
48 | /* Major opcode 2e (fused operations). */ |
49 | #define FP2E_SUBOP(i) (((i) >> 5) & 1) |
50 | #define FP2E_FORMAT(i) (((i) >> 11) & 1) |
51 | |
52 | /* Major opcode 26 (FMPYSUB) */ |
53 | /* Major opcode 06 (FMPYADD) */ |
54 | #define FPx6_FORMAT(i) ((i) & 0x1f) |
55 | |
56 | /* Flags and enable bits of the status word. */ |
57 | #define FPSW_FLAGS(w) ((w) >> 27) |
58 | #define FPSW_ENABLE(w) ((w) & 0x1f) |
59 | #define FPSW_V (1<<4) |
60 | #define FPSW_Z (1<<3) |
61 | #define FPSW_O (1<<2) |
62 | #define FPSW_U (1<<1) |
63 | #define FPSW_I (1<<0) |
64 | |
65 | /* Handle a floating point exception. Return zero if the faulting |
66 | instruction can be completed successfully. */ |
67 | int |
68 | handle_fpe(struct pt_regs *regs) |
69 | { |
70 | extern void printbinary(unsigned long x, int nbits); |
71 | unsigned int orig_sw, sw; |
72 | int signalcode; |
73 | /* need an intermediate copy of float regs because FPU emulation |
74 | * code expects an artificial last entry which contains zero |
75 | * |
76 | * also, the passed in fr registers contain one word that defines |
77 | * the fpu type. the fpu type information is constructed |
78 | * inside the emulation code |
79 | */ |
80 | __u64 frcopy[36]; |
81 | |
82 | memcpy(frcopy, regs->fr, sizeof regs->fr); |
83 | frcopy[32] = 0; |
84 | |
85 | memcpy(&orig_sw, frcopy, sizeof(orig_sw)); |
86 | |
87 | if (FPUDEBUG) { |
88 | printk(KERN_DEBUG "FP VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI ->\n " ); |
89 | printbinary(x: orig_sw, nbits: 32); |
90 | printk(KERN_DEBUG "\n" ); |
91 | } |
92 | |
93 | signalcode = decode_fpu(frcopy, 0x666); |
94 | |
95 | /* Status word = FR0L. */ |
96 | memcpy(&sw, frcopy, sizeof(sw)); |
97 | if (FPUDEBUG) { |
98 | printk(KERN_DEBUG "VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI decode_fpu returns %d|0x%x\n" , |
99 | signalcode >> 24, signalcode & 0xffffff); |
100 | printbinary(x: sw, nbits: 32); |
101 | printk(KERN_DEBUG "\n" ); |
102 | } |
103 | |
104 | memcpy(regs->fr, frcopy, sizeof regs->fr); |
105 | if (signalcode != 0) { |
106 | force_sig_fault(sig: signalcode >> 24, code: signalcode & 0xffffff, |
107 | addr: (void __user *) regs->iaoq[0]); |
108 | return -1; |
109 | } |
110 | |
111 | return signalcode ? -1 : 0; |
112 | } |
113 | |