math.c (10077B)
1// SPDX-License-Identifier: GPL-2.0-only 2#include <linux/module.h> 3#include <linux/types.h> 4#include <linux/kernel.h> 5#include <linux/sched.h> 6#include <asm/ptrace.h> 7 8#include <linux/uaccess.h> 9 10#include "sfp-util.h" 11#include <math-emu/soft-fp.h> 12#include <math-emu/single.h> 13#include <math-emu/double.h> 14 15#define OPC_PAL 0x00 16#define OPC_INTA 0x10 17#define OPC_INTL 0x11 18#define OPC_INTS 0x12 19#define OPC_INTM 0x13 20#define OPC_FLTC 0x14 21#define OPC_FLTV 0x15 22#define OPC_FLTI 0x16 23#define OPC_FLTL 0x17 24#define OPC_MISC 0x18 25#define OPC_JSR 0x1a 26 27#define FOP_SRC_S 0 28#define FOP_SRC_T 2 29#define FOP_SRC_Q 3 30 31#define FOP_FNC_ADDx 0 32#define FOP_FNC_CVTQL 0 33#define FOP_FNC_SUBx 1 34#define FOP_FNC_MULx 2 35#define FOP_FNC_DIVx 3 36#define FOP_FNC_CMPxUN 4 37#define FOP_FNC_CMPxEQ 5 38#define FOP_FNC_CMPxLT 6 39#define FOP_FNC_CMPxLE 7 40#define FOP_FNC_SQRTx 11 41#define FOP_FNC_CVTxS 12 42#define FOP_FNC_CVTxT 14 43#define FOP_FNC_CVTxQ 15 44 45#define MISC_TRAPB 0x0000 46#define MISC_EXCB 0x0400 47 48extern unsigned long alpha_read_fp_reg (unsigned long reg); 49extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); 50extern unsigned long alpha_read_fp_reg_s (unsigned long reg); 51extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val); 52 53 54#ifdef MODULE 55 56MODULE_DESCRIPTION("FP Software completion module"); 57MODULE_LICENSE("GPL v2"); 58 59extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); 60extern long (*alpha_fp_emul) (unsigned long pc); 61 62static long (*save_emul_imprecise)(struct pt_regs *, unsigned long); 63static long (*save_emul) (unsigned long pc); 64 65long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long); 66long do_alpha_fp_emul(unsigned long); 67 68static int alpha_fp_emul_init_module(void) 69{ 70 save_emul_imprecise = alpha_fp_emul_imprecise; 71 save_emul = alpha_fp_emul; 72 alpha_fp_emul_imprecise = do_alpha_fp_emul_imprecise; 73 alpha_fp_emul = do_alpha_fp_emul; 74 return 0; 75} 76module_init(alpha_fp_emul_init_module); 77 78static void alpha_fp_emul_cleanup_module(void) 79{ 80 alpha_fp_emul_imprecise = save_emul_imprecise; 81 alpha_fp_emul = save_emul; 82} 83module_exit(alpha_fp_emul_cleanup_module); 84 85#undef alpha_fp_emul_imprecise 86#define alpha_fp_emul_imprecise do_alpha_fp_emul_imprecise 87#undef alpha_fp_emul 88#define alpha_fp_emul do_alpha_fp_emul 89 90#endif /* MODULE */ 91 92 93/* 94 * Emulate the floating point instruction at address PC. Returns -1 if the 95 * instruction to be emulated is illegal (such as with the opDEC trap), else 96 * the SI_CODE for a SIGFPE signal, else 0 if everything's ok. 97 * 98 * Notice that the kernel does not and cannot use FP regs. This is good 99 * because it means that instead of saving/restoring all fp regs, we simply 100 * stick the result of the operation into the appropriate register. 101 */ 102long 103alpha_fp_emul (unsigned long pc) 104{ 105 FP_DECL_EX; 106 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); 107 FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); 108 109 unsigned long fa, fb, fc, func, mode, src; 110 unsigned long res, va, vb, vc, swcr, fpcr; 111 __u32 insn; 112 long si_code; 113 114 get_user(insn, (__u32 __user *)pc); 115 fc = (insn >> 0) & 0x1f; /* destination register */ 116 fb = (insn >> 16) & 0x1f; 117 fa = (insn >> 21) & 0x1f; 118 func = (insn >> 5) & 0xf; 119 src = (insn >> 9) & 0x3; 120 mode = (insn >> 11) & 0x3; 121 122 fpcr = rdfpcr(); 123 swcr = swcr_update_status(current_thread_info()->ieee_state, fpcr); 124 125 if (mode == 3) { 126 /* Dynamic -- get rounding mode from fpcr. */ 127 mode = (fpcr >> FPCR_DYN_SHIFT) & 3; 128 } 129 130 switch (src) { 131 case FOP_SRC_S: 132 va = alpha_read_fp_reg_s(fa); 133 vb = alpha_read_fp_reg_s(fb); 134 135 FP_UNPACK_SP(SA, &va); 136 FP_UNPACK_SP(SB, &vb); 137 138 switch (func) { 139 case FOP_FNC_SUBx: 140 FP_SUB_S(SR, SA, SB); 141 goto pack_s; 142 143 case FOP_FNC_ADDx: 144 FP_ADD_S(SR, SA, SB); 145 goto pack_s; 146 147 case FOP_FNC_MULx: 148 FP_MUL_S(SR, SA, SB); 149 goto pack_s; 150 151 case FOP_FNC_DIVx: 152 FP_DIV_S(SR, SA, SB); 153 goto pack_s; 154 155 case FOP_FNC_SQRTx: 156 FP_SQRT_S(SR, SB); 157 goto pack_s; 158 } 159 goto bad_insn; 160 161 case FOP_SRC_T: 162 va = alpha_read_fp_reg(fa); 163 vb = alpha_read_fp_reg(fb); 164 165 if ((func & ~3) == FOP_FNC_CMPxUN) { 166 FP_UNPACK_RAW_DP(DA, &va); 167 FP_UNPACK_RAW_DP(DB, &vb); 168 if (!DA_e && !_FP_FRAC_ZEROP_1(DA)) { 169 FP_SET_EXCEPTION(FP_EX_DENORM); 170 if (FP_DENORM_ZERO) 171 _FP_FRAC_SET_1(DA, _FP_ZEROFRAC_1); 172 } 173 if (!DB_e && !_FP_FRAC_ZEROP_1(DB)) { 174 FP_SET_EXCEPTION(FP_EX_DENORM); 175 if (FP_DENORM_ZERO) 176 _FP_FRAC_SET_1(DB, _FP_ZEROFRAC_1); 177 } 178 FP_CMP_D(res, DA, DB, 3); 179 vc = 0x4000000000000000UL; 180 /* CMPTEQ, CMPTUN don't trap on QNaN, 181 while CMPTLT and CMPTLE do */ 182 if (res == 3 183 && ((func & 3) >= 2 184 || FP_ISSIGNAN_D(DA) 185 || FP_ISSIGNAN_D(DB))) { 186 FP_SET_EXCEPTION(FP_EX_INVALID); 187 } 188 switch (func) { 189 case FOP_FNC_CMPxUN: if (res != 3) vc = 0; break; 190 case FOP_FNC_CMPxEQ: if (res) vc = 0; break; 191 case FOP_FNC_CMPxLT: if (res != -1) vc = 0; break; 192 case FOP_FNC_CMPxLE: if ((long)res > 0) vc = 0; break; 193 } 194 goto done_d; 195 } 196 197 FP_UNPACK_DP(DA, &va); 198 FP_UNPACK_DP(DB, &vb); 199 200 switch (func) { 201 case FOP_FNC_SUBx: 202 FP_SUB_D(DR, DA, DB); 203 goto pack_d; 204 205 case FOP_FNC_ADDx: 206 FP_ADD_D(DR, DA, DB); 207 goto pack_d; 208 209 case FOP_FNC_MULx: 210 FP_MUL_D(DR, DA, DB); 211 goto pack_d; 212 213 case FOP_FNC_DIVx: 214 FP_DIV_D(DR, DA, DB); 215 goto pack_d; 216 217 case FOP_FNC_SQRTx: 218 FP_SQRT_D(DR, DB); 219 goto pack_d; 220 221 case FOP_FNC_CVTxS: 222 /* It is irritating that DEC encoded CVTST with 223 SRC == T_floating. It is also interesting that 224 the bit used to tell the two apart is /U... */ 225 if (insn & 0x2000) { 226 FP_CONV(S,D,1,1,SR,DB); 227 goto pack_s; 228 } else { 229 vb = alpha_read_fp_reg_s(fb); 230 FP_UNPACK_SP(SB, &vb); 231 DR_c = DB_c; 232 DR_s = DB_s; 233 DR_e = DB_e + (1024 - 128); 234 DR_f = SB_f << (52 - 23); 235 goto pack_d; 236 } 237 238 case FOP_FNC_CVTxQ: 239 if (DB_c == FP_CLS_NAN 240 && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D)) { 241 /* AAHB Table B-2 says QNaN should not trigger INV */ 242 vc = 0; 243 } else 244 FP_TO_INT_ROUND_D(vc, DB, 64, 2); 245 goto done_d; 246 } 247 goto bad_insn; 248 249 case FOP_SRC_Q: 250 vb = alpha_read_fp_reg(fb); 251 252 switch (func) { 253 case FOP_FNC_CVTQL: 254 /* Notice: We can get here only due to an integer 255 overflow. Such overflows are reported as invalid 256 ops. We return the result the hw would have 257 computed. */ 258 vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ 259 (vb & 0x3fffffff) << 29); /* rest of the int */ 260 FP_SET_EXCEPTION (FP_EX_INVALID); 261 goto done_d; 262 263 case FOP_FNC_CVTxS: 264 FP_FROM_INT_S(SR, ((long)vb), 64, long); 265 goto pack_s; 266 267 case FOP_FNC_CVTxT: 268 FP_FROM_INT_D(DR, ((long)vb), 64, long); 269 goto pack_d; 270 } 271 goto bad_insn; 272 } 273 goto bad_insn; 274 275pack_s: 276 FP_PACK_SP(&vc, SR); 277 if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ)) 278 vc = 0; 279 alpha_write_fp_reg_s(fc, vc); 280 goto done; 281 282pack_d: 283 FP_PACK_DP(&vc, DR); 284 if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ)) 285 vc = 0; 286done_d: 287 alpha_write_fp_reg(fc, vc); 288 goto done; 289 290 /* 291 * Take the appropriate action for each possible 292 * floating-point result: 293 * 294 * - Set the appropriate bits in the FPCR 295 * - If the specified exception is enabled in the FPCR, 296 * return. The caller (entArith) will dispatch 297 * the appropriate signal to the translated program. 298 * 299 * In addition, properly track the exception state in software 300 * as described in the Alpha Architecture Handbook section 4.7.7.3. 301 */ 302done: 303 if (_fex) { 304 /* Record exceptions in software control word. */ 305 swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); 306 current_thread_info()->ieee_state 307 |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); 308 309 /* Update hardware control register. */ 310 fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); 311 fpcr |= ieee_swcr_to_fpcr(swcr); 312 wrfpcr(fpcr); 313 314 /* Do we generate a signal? */ 315 _fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK; 316 si_code = 0; 317 if (_fex) { 318 if (_fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND; 319 if (_fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES; 320 if (_fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND; 321 if (_fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF; 322 if (_fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV; 323 if (_fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV; 324 } 325 326 return si_code; 327 } 328 329 /* We used to write the destination register here, but DEC FORTRAN 330 requires that the result *always* be written... so we do the write 331 immediately after the operations above. */ 332 333 return 0; 334 335bad_insn: 336 printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", 337 insn, pc); 338 return -1; 339} 340 341long 342alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) 343{ 344 unsigned long trigger_pc = regs->pc - 4; 345 unsigned long insn, opcode, rc, si_code = 0; 346 347 /* 348 * Turn off the bits corresponding to registers that are the 349 * target of instructions that set bits in the exception 350 * summary register. We have some slack doing this because a 351 * register that is the target of a trapping instruction can 352 * be written at most once in the trap shadow. 353 * 354 * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all 355 * bound the trap shadow, so we need not look any further than 356 * up to the first occurrence of such an instruction. 357 */ 358 while (write_mask) { 359 get_user(insn, (__u32 __user *)(trigger_pc)); 360 opcode = insn >> 26; 361 rc = insn & 0x1f; 362 363 switch (opcode) { 364 case OPC_PAL: 365 case OPC_JSR: 366 case 0x30 ... 0x3f: /* branches */ 367 goto egress; 368 369 case OPC_MISC: 370 switch (insn & 0xffff) { 371 case MISC_TRAPB: 372 case MISC_EXCB: 373 goto egress; 374 375 default: 376 break; 377 } 378 break; 379 380 case OPC_INTA: 381 case OPC_INTL: 382 case OPC_INTS: 383 case OPC_INTM: 384 write_mask &= ~(1UL << rc); 385 break; 386 387 case OPC_FLTC: 388 case OPC_FLTV: 389 case OPC_FLTI: 390 case OPC_FLTL: 391 write_mask &= ~(1UL << (rc + 32)); 392 break; 393 } 394 if (!write_mask) { 395 /* Re-execute insns in the trap-shadow. */ 396 regs->pc = trigger_pc + 4; 397 si_code = alpha_fp_emul(trigger_pc); 398 goto egress; 399 } 400 trigger_pc -= 4; 401 } 402 403egress: 404 return si_code; 405}