root/arch/alpha/math-emu/fp-emul.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. alpha_read_fp_reg
  2. alpha_write_fp_reg
  3. alpha_fp_emul
  4. alpha_fp_emul_imprecise

   1 #include <linux/types.h>
   2 
   3 #include <linux/kernel.h>
   4 #include <linux/sched.h>
   5 
   6 #include <asm/segment.h>
   7 
   8 #include "ieee-math.h"
   9 
  10 #define OPC_PAL         0x00
  11 
  12 #define OPC_INTA        0x10
  13 #define OPC_INTL        0x11
  14 #define OPC_INTS        0x12
  15 #define OPC_INTM        0x13
  16 #define OPC_FLTV        0x14
  17 #define OPC_FLTI        0x15
  18 #define OPC_FLTL        0x16
  19 
  20 #define OPC_MISC        0x18
  21 
  22 #define OPC_JSR         0x1a
  23 
  24 /*
  25  * "Base" function codes for the FLTI-class instructions.  These
  26  * instructions all have opcode 0x16.  Note that in most cases these
  27  * actually correspond to the "chopped" form of the instruction.  Not
  28  * to worry---we extract the qualifier bits separately and deal with
  29  * them separately.  Notice that base function code 0x2c is used for
  30  * both CVTTS and CVTST.  The other bits in the function code are used
  31  * to distinguish the two.
  32  */
  33 #define FLTI_FUNC_ADDS                  0x000
  34 #define FLTI_FUNC_ADDT                  0x020
  35 #define FLTI_FUNC_CMPTEQ                0x025
  36 #define FLTI_FUNC_CMPTLT                0x026
  37 #define FLTI_FUNC_CMPTLE                0x027
  38 #define FLTI_FUNC_CMPTUN                0x024
  39 #define FLTI_FUNC_CVTTS_or_CVTST        0x02c
  40 #define FLTI_FUNC_CVTTQ                 0x02f
  41 #define FLTI_FUNC_CVTQS                 0x03c
  42 #define FLTI_FUNC_CVTQT                 0x03e
  43 #define FLTI_FUNC_DIVS                  0x003
  44 #define FLTI_FUNC_DIVT                  0x023
  45 #define FLTI_FUNC_MULS                  0x002
  46 #define FLTI_FUNC_MULT                  0x022
  47 #define FLTI_FUNC_SUBS                  0x001
  48 #define FLTI_FUNC_SUBT                  0x021
  49 
  50 #define FLTI_FUNC_CVTQL                 0x030   /* opcode 0x17 */
  51 
  52 #define MISC_TRAPB      0x0000
  53 #define MISC_EXCB       0x0400
  54 
  55 
  56 extern unsigned long    rdfpcr (void);
  57 extern void             wrfpcr (unsigned long);
  58 
  59 
  60 unsigned long
  61 alpha_read_fp_reg (unsigned long reg)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63         unsigned long r;
  64 
  65         switch (reg) {
  66               case  0: asm ("stt  $f0,%0" : "m="(r)); break;
  67               case  1: asm ("stt  $f1,%0" : "m="(r)); break;
  68               case  2: asm ("stt  $f2,%0" : "m="(r)); break;
  69               case  3: asm ("stt  $f3,%0" : "m="(r)); break;
  70               case  4: asm ("stt  $f4,%0" : "m="(r)); break;
  71               case  5: asm ("stt  $f5,%0" : "m="(r)); break;
  72               case  6: asm ("stt  $f6,%0" : "m="(r)); break;
  73               case  7: asm ("stt  $f7,%0" : "m="(r)); break;
  74               case  8: asm ("stt  $f8,%0" : "m="(r)); break;
  75               case  9: asm ("stt  $f9,%0" : "m="(r)); break;
  76               case 10: asm ("stt $f10,%0" : "m="(r)); break;
  77               case 11: asm ("stt $f11,%0" : "m="(r)); break;
  78               case 12: asm ("stt $f12,%0" : "m="(r)); break;
  79               case 13: asm ("stt $f13,%0" : "m="(r)); break;
  80               case 14: asm ("stt $f14,%0" : "m="(r)); break;
  81               case 15: asm ("stt $f15,%0" : "m="(r)); break;
  82               case 16: asm ("stt $f16,%0" : "m="(r)); break;
  83               case 17: asm ("stt $f17,%0" : "m="(r)); break;
  84               case 18: asm ("stt $f18,%0" : "m="(r)); break;
  85               case 19: asm ("stt $f19,%0" : "m="(r)); break;
  86               case 20: asm ("stt $f20,%0" : "m="(r)); break;
  87               case 21: asm ("stt $f21,%0" : "m="(r)); break;
  88               case 22: asm ("stt $f22,%0" : "m="(r)); break;
  89               case 23: asm ("stt $f23,%0" : "m="(r)); break;
  90               case 24: asm ("stt $f24,%0" : "m="(r)); break;
  91               case 25: asm ("stt $f25,%0" : "m="(r)); break;
  92               case 26: asm ("stt $f26,%0" : "m="(r)); break;
  93               case 27: asm ("stt $f27,%0" : "m="(r)); break;
  94               case 28: asm ("stt $f28,%0" : "m="(r)); break;
  95               case 29: asm ("stt $f29,%0" : "m="(r)); break;
  96               case 30: asm ("stt $f30,%0" : "m="(r)); break;
  97               case 31: asm ("stt $f31,%0" : "m="(r)); break;
  98               default:
  99                 break;
 100         }
 101         return r;
 102 }
 103 
 104 
 105 #if 0
 106 /*
 107  * This is IMHO the better way of implementing LDT().  But it
 108  * has the disadvantage that gcc 2.7.0 refuses to compile it
 109  * (invalid operand constraints), so instead, we use the uglier
 110  * macro below.
 111  */
 112 # define LDT(reg,val)   \
 113   asm volatile ("ldt $f"#reg",%0" :: "m"(val));
 114 #else
 115 # define LDT(reg,val)   \
 116   asm volatile ("ldt $f"#reg",0(%0)" :: "r"(&val));
 117 #endif
 118 
 119 void
 120 alpha_write_fp_reg (unsigned long reg, unsigned long val)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122         switch (reg) {
 123               case  0: LDT( 0, val); break;
 124               case  1: LDT( 1, val); break;
 125               case  2: LDT( 2, val); break;
 126               case  3: LDT( 3, val); break;
 127               case  4: LDT( 4, val); break;
 128               case  5: LDT( 5, val); break;
 129               case  6: LDT( 6, val); break;
 130               case  7: LDT( 7, val); break;
 131               case  8: LDT( 8, val); break;
 132               case  9: LDT( 9, val); break;
 133               case 10: LDT(10, val); break;
 134               case 11: LDT(11, val); break;
 135               case 12: LDT(12, val); break;
 136               case 13: LDT(13, val); break;
 137               case 14: LDT(14, val); break;
 138               case 15: LDT(15, val); break;
 139               case 16: LDT(16, val); break;
 140               case 17: LDT(17, val); break;
 141               case 18: LDT(18, val); break;
 142               case 19: LDT(19, val); break;
 143               case 20: LDT(20, val); break;
 144               case 21: LDT(21, val); break;
 145               case 22: LDT(22, val); break;
 146               case 23: LDT(23, val); break;
 147               case 24: LDT(24, val); break;
 148               case 25: LDT(25, val); break;
 149               case 26: LDT(26, val); break;
 150               case 27: LDT(27, val); break;
 151               case 28: LDT(28, val); break;
 152               case 29: LDT(29, val); break;
 153               case 30: LDT(30, val); break;
 154               case 31: LDT(31, val); break;
 155               default:
 156                 break;
 157         }
 158 }
 159 
 160 
 161 /*
 162  * Emulate the floating point instruction at address PC.  Returns 0 if
 163  * emulation fails.  Notice that the kernel does not and cannot use FP
 164  * regs.  This is good because it means that instead of
 165  * saving/restoring all fp regs, we simply stick the result of the
 166  * operation into the appropriate register.
 167  */
 168 long
 169 alpha_fp_emul (unsigned long pc)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171         unsigned long opcode, fa, fb, fc, func, mode;
 172         unsigned long fpcw = current->tss.flags;
 173         unsigned long va, vb, vc, res, fpcr;
 174         __u32 insn;
 175 
 176         insn = get_user((__u32*)pc);
 177         fc     = (insn >>  0) &  0x1f;  /* destination register */
 178         func   = (insn >>  5) & 0x3ff;
 179         fb     = (insn >> 16) &  0x1f;
 180         fa     = (insn >> 21) &  0x1f;
 181         opcode = insn >> 26;
 182         
 183         va = alpha_read_fp_reg(fa);
 184         vb = alpha_read_fp_reg(fb);
 185 
 186         fpcr = rdfpcr();
 187         /*
 188          * Try the operation in software.  First, obtain the rounding
 189          * mode...
 190          */
 191         mode = func & 0xc0;
 192         if (mode == 0xc0) {
 193             /* dynamic---get rounding mode from fpcr: */
 194             mode = ((fpcr & FPCR_DYN_MASK) >> FPCR_DYN_SHIFT) << ROUND_SHIFT;
 195         }
 196         mode |= (fpcw & IEEE_TRAP_ENABLE_MASK);
 197 
 198         if ((IEEE_TRAP_ENABLE_MASK & 0xc0)) {
 199                 extern int something_is_wrong (void);
 200                 something_is_wrong();
 201         }
 202 
 203         /* least 6 bits contain operation code: */
 204         switch (func & 0x3f) {
 205               case FLTI_FUNC_CMPTEQ:
 206                 res = ieee_CMPTEQ(va, vb, &vc);
 207                 break;
 208 
 209               case FLTI_FUNC_CMPTLT:
 210                 res = ieee_CMPTLT(va, vb, &vc);
 211                 break;
 212 
 213               case FLTI_FUNC_CMPTLE:
 214                 res = ieee_CMPTLE(va, vb, &vc);
 215                 break;
 216 
 217               case FLTI_FUNC_CMPTUN:
 218                 res = ieee_CMPTUN(va, vb, &vc);
 219                 break;
 220 
 221               case FLTI_FUNC_CVTQL:
 222                 /*
 223                  * Notice: We can get here only due to an integer
 224                  * overflow.  Such overflows are reported as invalid
 225                  * ops.  We return the result the hw would have
 226                  * computed.
 227                  */
 228                 vc = ((vb & 0xc0000000) << 32 | /* sign and msb */
 229                       (vb & 0x3fffffff) << 29); /* rest of the integer */
 230                 res = FPCR_INV;
 231                 break;
 232 
 233               case FLTI_FUNC_CVTQS:
 234                 res = ieee_CVTQS(mode, vb, &vc);
 235                 break;
 236 
 237               case FLTI_FUNC_CVTQT:
 238                 res = ieee_CVTQT(mode, vb, &vc);
 239                 break;
 240 
 241               case FLTI_FUNC_CVTTS_or_CVTST:
 242                 if (func == 0x6ac) {
 243                         /*
 244                          * 0x2ac is also CVTST, but if the /S
 245                          * qualifier isn't set, we wouldn't be here in
 246                          * the first place...
 247                          */
 248                         res = ieee_CVTST(mode, vb, &vc);
 249                 } else {
 250                         res = ieee_CVTTS(mode, vb, &vc);
 251                 }
 252                 break; 
 253 
 254               case FLTI_FUNC_DIVS:
 255                 res = ieee_DIVS(mode, va, vb, &vc);
 256                 break;
 257 
 258               case FLTI_FUNC_DIVT:
 259                 res = ieee_DIVT(mode, va, vb, &vc);
 260                 break;
 261 
 262               case FLTI_FUNC_MULS:
 263                 res = ieee_MULS(mode, va, vb, &vc);
 264                 break;
 265 
 266               case FLTI_FUNC_MULT:
 267                 res = ieee_MULT(mode, va, vb, &vc);
 268                 break;
 269 
 270               case FLTI_FUNC_SUBS:
 271                 res = ieee_SUBS(mode, va, vb, &vc);
 272                 break;
 273 
 274               case FLTI_FUNC_SUBT:
 275                 res = ieee_SUBT(mode, va, vb, &vc);
 276                 break;
 277 
 278               case FLTI_FUNC_ADDS:
 279                 res = ieee_ADDS(mode, va, vb, &vc);
 280                 break;
 281 
 282               case FLTI_FUNC_ADDT:
 283                 res = ieee_ADDT(mode, va, vb, &vc);
 284                 break;
 285 
 286               case FLTI_FUNC_CVTTQ:
 287                 res = ieee_CVTTQ(mode, vb, &vc);
 288                 break;
 289 
 290               default:
 291                 printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n",
 292                        func & 0x3f, pc);
 293                 return 0;
 294         }
 295         /*
 296          * Take the appropriate action for each possible
 297          * floating-point result:
 298          *
 299          *      - Set the appropriate bits in the FPCR
 300          *      - If the specified exception is enabled in the FPCR,
 301          *        return.  The caller (mxr_signal_handler) will dispatch
 302          *        the appropriate signal to the translated program.
 303          */
 304         if (res) {
 305                 fpcr |= FPCR_SUM | res;
 306                 wrfpcr(fpcr);
 307                 if (((res & FPCR_INV) && (fpcw & IEEE_TRAP_ENABLE_INV)) ||
 308                     ((res & FPCR_DZE) && (fpcw & IEEE_TRAP_ENABLE_DZE)) ||
 309                     ((res & FPCR_OVF) && (fpcw & IEEE_TRAP_ENABLE_OVF)) ||
 310                     ((res & FPCR_UNF) && (fpcw & IEEE_TRAP_ENABLE_UNF)) ||
 311                     ((res & FPCR_INE) && (fpcw & IEEE_TRAP_ENABLE_INE)))
 312                         return 0;
 313         }
 314         /*
 315          * Whoo-kay... we got this far, and we're not generating a signal
 316          * to the translated program.  All that remains is to write the
 317          * result:
 318          */
 319         alpha_write_fp_reg(fc, vc);
 320         return 1;
 321 }
 322 
 323 
 324 long
 325 alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327         unsigned long trigger_pc = regs->pc - 4;
 328         unsigned long insn, opcode, rc;
 329         /*
 330          * Turn off the bits corresponding to registers that are the
 331          * target of instructions that set bits in the exception
 332          * summary register.  We have some slack doing this because a
 333          * register that is the target of a trapping instruction can
 334          * be written at most once in the trap shadow.
 335          *
 336          * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all
 337          * bound the trap shadow, so we need not look any further than
 338          * up to the first occurance of such an instruction.
 339          */
 340         while (write_mask) {
 341                 insn = get_user((__u32*)(trigger_pc));
 342                 opcode = insn >> 26;
 343                 rc = insn & 0x1f;
 344 
 345                 switch (opcode) {
 346                       case OPC_PAL:
 347                       case OPC_JSR:
 348                       case 0x30 ... 0x3f:       /* branches */
 349                         return 0;
 350 
 351                       case OPC_MISC:
 352                         switch (insn & 0xffff) {
 353                               case MISC_TRAPB:
 354                               case MISC_EXCB:
 355                                 return 0;
 356 
 357                               default:
 358                                 break;
 359                         }
 360                         break;
 361 
 362                       case OPC_INTA:
 363                       case OPC_INTL:
 364                       case OPC_INTS:
 365                       case OPC_INTM:
 366                         write_mask &= ~(1UL << rc);
 367                         break;
 368 
 369                       case OPC_FLTV:
 370                       case OPC_FLTI:
 371                       case OPC_FLTL:
 372                         write_mask &= ~(1UL << (rc + 32));
 373                         break;
 374                 }
 375                 if (!write_mask) {
 376                         if ((opcode == OPC_FLTI || opcode == OPC_FLTL)
 377                             && alpha_fp_emul(trigger_pc))
 378                         {
 379                             /* re-execute insns in trap-shadow: */
 380                             regs->pc = trigger_pc + 4;
 381                             return 1;
 382                         }
 383                         break;
 384                 }
 385                 trigger_pc -= 4;
 386         }
 387         return 0;
 388 }

/* [previous][next][first][last][top][bottom][index][help] */