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