Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / arch / powerpc / kernel / vecemu.c
diff --git a/kernel/arch/powerpc/kernel/vecemu.c b/kernel/arch/powerpc/kernel/vecemu.c
new file mode 100644 (file)
index 0000000..c4bfadb
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Routines to emulate some Altivec/VMX instructions, specifically
+ * those that can trap when given denormalized operands in Java mode.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+/* Functions in vector.S */
+extern void vaddfp(vector128 *dst, vector128 *a, vector128 *b);
+extern void vsubfp(vector128 *dst, vector128 *a, vector128 *b);
+extern void vmaddfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
+extern void vnmsubfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
+extern void vrefp(vector128 *dst, vector128 *src);
+extern void vrsqrtefp(vector128 *dst, vector128 *src);
+extern void vexptep(vector128 *dst, vector128 *src);
+
+static unsigned int exp2s[8] = {
+       0x800000,
+       0x8b95c2,
+       0x9837f0,
+       0xa5fed7,
+       0xb504f3,
+       0xc5672a,
+       0xd744fd,
+       0xeac0c7
+};
+
+/*
+ * Computes an estimate of 2^x.  The `s' argument is the 32-bit
+ * single-precision floating-point representation of x.
+ */
+static unsigned int eexp2(unsigned int s)
+{
+       int exp, pwr;
+       unsigned int mant, frac;
+
+       /* extract exponent field from input */
+       exp = ((s >> 23) & 0xff) - 127;
+       if (exp > 7) {
+               /* check for NaN input */
+               if (exp == 128 && (s & 0x7fffff) != 0)
+                       return s | 0x400000;    /* return QNaN */
+               /* 2^-big = 0, 2^+big = +Inf */
+               return (s & 0x80000000)? 0: 0x7f800000; /* 0 or +Inf */
+       }
+       if (exp < -23)
+               return 0x3f800000;      /* 1.0 */
+
+       /* convert to fixed point integer in 9.23 representation */
+       pwr = (s & 0x7fffff) | 0x800000;
+       if (exp > 0)
+               pwr <<= exp;
+       else
+               pwr >>= -exp;
+       if (s & 0x80000000)
+               pwr = -pwr;
+
+       /* extract integer part, which becomes exponent part of result */
+       exp = (pwr >> 23) + 126;
+       if (exp >= 254)
+               return 0x7f800000;
+       if (exp < -23)
+               return 0;
+
+       /* table lookup on top 3 bits of fraction to get mantissa */
+       mant = exp2s[(pwr >> 20) & 7];
+
+       /* linear interpolation using remaining 20 bits of fraction */
+       asm("mulhwu %0,%1,%2" : "=r" (frac)
+           : "r" (pwr << 12), "r" (0x172b83ff));
+       asm("mulhwu %0,%1,%2" : "=r" (frac) : "r" (frac), "r" (mant));
+       mant += frac;
+
+       if (exp >= 0)
+               return mant + (exp << 23);
+
+       /* denormalized result */
+       exp = -exp;
+       mant += 1 << (exp - 1);
+       return mant >> exp;
+}
+
+/*
+ * Computes an estimate of log_2(x).  The `s' argument is the 32-bit
+ * single-precision floating-point representation of x.
+ */
+static unsigned int elog2(unsigned int s)
+{
+       int exp, mant, lz, frac;
+
+       exp = s & 0x7f800000;
+       mant = s & 0x7fffff;
+       if (exp == 0x7f800000) {        /* Inf or NaN */
+               if (mant != 0)
+                       s |= 0x400000;  /* turn NaN into QNaN */
+               return s;
+       }
+       if ((exp | mant) == 0)          /* +0 or -0 */
+               return 0xff800000;      /* return -Inf */
+
+       if (exp == 0) {
+               /* denormalized */
+               asm("cntlzw %0,%1" : "=r" (lz) : "r" (mant));
+               mant <<= lz - 8;
+               exp = (-118 - lz) << 23;
+       } else {
+               mant |= 0x800000;
+               exp -= 127 << 23;
+       }
+
+       if (mant >= 0xb504f3) {                         /* 2^0.5 * 2^23 */
+               exp |= 0x400000;                        /* 0.5 * 2^23 */
+               asm("mulhwu %0,%1,%2" : "=r" (mant)
+                   : "r" (mant), "r" (0xb504f334));    /* 2^-0.5 * 2^32 */
+       }
+       if (mant >= 0x9837f0) {                         /* 2^0.25 * 2^23 */
+               exp |= 0x200000;                        /* 0.25 * 2^23 */
+               asm("mulhwu %0,%1,%2" : "=r" (mant)
+                   : "r" (mant), "r" (0xd744fccb));    /* 2^-0.25 * 2^32 */
+       }
+       if (mant >= 0x8b95c2) {                         /* 2^0.125 * 2^23 */
+               exp |= 0x100000;                        /* 0.125 * 2^23 */
+               asm("mulhwu %0,%1,%2" : "=r" (mant)
+                   : "r" (mant), "r" (0xeac0c6e8));    /* 2^-0.125 * 2^32 */
+       }
+       if (mant > 0x800000) {                          /* 1.0 * 2^23 */
+               /* calculate (mant - 1) * 1.381097463 */
+               /* 1.381097463 == 0.125 / (2^0.125 - 1) */
+               asm("mulhwu %0,%1,%2" : "=r" (frac)
+                   : "r" ((mant - 0x800000) << 1), "r" (0xb0c7cd3a));
+               exp += frac;
+       }
+       s = exp & 0x80000000;
+       if (exp != 0) {
+               if (s)
+                       exp = -exp;
+               asm("cntlzw %0,%1" : "=r" (lz) : "r" (exp));
+               lz = 8 - lz;
+               if (lz > 0)
+                       exp >>= lz;
+               else if (lz < 0)
+                       exp <<= -lz;
+               s += ((lz + 126) << 23) + exp;
+       }
+       return s;
+}
+
+#define VSCR_SAT       1
+
+static int ctsxs(unsigned int x, int scale, unsigned int *vscrp)
+{
+       int exp, mant;
+
+       exp = (x >> 23) & 0xff;
+       mant = x & 0x7fffff;
+       if (exp == 255 && mant != 0)
+               return 0;               /* NaN -> 0 */
+       exp = exp - 127 + scale;
+       if (exp < 0)
+               return 0;               /* round towards zero */
+       if (exp >= 31) {
+               /* saturate, unless the result would be -2^31 */
+               if (x + (scale << 23) != 0xcf000000)
+                       *vscrp |= VSCR_SAT;
+               return (x & 0x80000000)? 0x80000000: 0x7fffffff;
+       }
+       mant |= 0x800000;
+       mant = (mant << 7) >> (30 - exp);
+       return (x & 0x80000000)? -mant: mant;
+}
+
+static unsigned int ctuxs(unsigned int x, int scale, unsigned int *vscrp)
+{
+       int exp;
+       unsigned int mant;
+
+       exp = (x >> 23) & 0xff;
+       mant = x & 0x7fffff;
+       if (exp == 255 && mant != 0)
+               return 0;               /* NaN -> 0 */
+       exp = exp - 127 + scale;
+       if (exp < 0)
+               return 0;               /* round towards zero */
+       if (x & 0x80000000) {
+               /* negative => saturate to 0 */
+               *vscrp |= VSCR_SAT;
+               return 0;
+       }
+       if (exp >= 32) {
+               /* saturate */
+               *vscrp |= VSCR_SAT;
+               return 0xffffffff;
+       }
+       mant |= 0x800000;
+       mant = (mant << 8) >> (31 - exp);
+       return mant;
+}
+
+/* Round to floating integer, towards 0 */
+static unsigned int rfiz(unsigned int x)
+{
+       int exp;
+
+       exp = ((x >> 23) & 0xff) - 127;
+       if (exp == 128 && (x & 0x7fffff) != 0)
+               return x | 0x400000;    /* NaN -> make it a QNaN */
+       if (exp >= 23)
+               return x;               /* it's an integer already (or Inf) */
+       if (exp < 0)
+               return x & 0x80000000;  /* |x| < 1.0 rounds to 0 */
+       return x & ~(0x7fffff >> exp);
+}
+
+/* Round to floating integer, towards +/- Inf */
+static unsigned int rfii(unsigned int x)
+{
+       int exp, mask;
+
+       exp = ((x >> 23) & 0xff) - 127;
+       if (exp == 128 && (x & 0x7fffff) != 0)
+               return x | 0x400000;    /* NaN -> make it a QNaN */
+       if (exp >= 23)
+               return x;               /* it's an integer already (or Inf) */
+       if ((x & 0x7fffffff) == 0)
+               return x;               /* +/-0 -> +/-0 */
+       if (exp < 0)
+               /* 0 < |x| < 1.0 rounds to +/- 1.0 */
+               return (x & 0x80000000) | 0x3f800000;
+       mask = 0x7fffff >> exp;
+       /* mantissa overflows into exponent - that's OK,
+          it can't overflow into the sign bit */
+       return (x + mask) & ~mask;
+}
+
+/* Round to floating integer, to nearest */
+static unsigned int rfin(unsigned int x)
+{
+       int exp, half;
+
+       exp = ((x >> 23) & 0xff) - 127;
+       if (exp == 128 && (x & 0x7fffff) != 0)
+               return x | 0x400000;    /* NaN -> make it a QNaN */
+       if (exp >= 23)
+               return x;               /* it's an integer already (or Inf) */
+       if (exp < -1)
+               return x & 0x80000000;  /* |x| < 0.5 -> +/-0 */
+       if (exp == -1)
+               /* 0.5 <= |x| < 1.0 rounds to +/- 1.0 */
+               return (x & 0x80000000) | 0x3f800000;
+       half = 0x400000 >> exp;
+       /* add 0.5 to the magnitude and chop off the fraction bits */
+       return (x + half) & ~(0x7fffff >> exp);
+}
+
+int emulate_altivec(struct pt_regs *regs)
+{
+       unsigned int instr, i;
+       unsigned int va, vb, vc, vd;
+       vector128 *vrs;
+
+       if (get_user(instr, (unsigned int __user *) regs->nip))
+               return -EFAULT;
+       if ((instr >> 26) != 4)
+               return -EINVAL;         /* not an altivec instruction */
+       vd = (instr >> 21) & 0x1f;
+       va = (instr >> 16) & 0x1f;
+       vb = (instr >> 11) & 0x1f;
+       vc = (instr >> 6) & 0x1f;
+
+       vrs = current->thread.vr_state.vr;
+       switch (instr & 0x3f) {
+       case 10:
+               switch (vc) {
+               case 0: /* vaddfp */
+                       vaddfp(&vrs[vd], &vrs[va], &vrs[vb]);
+                       break;
+               case 1: /* vsubfp */
+                       vsubfp(&vrs[vd], &vrs[va], &vrs[vb]);
+                       break;
+               case 4: /* vrefp */
+                       vrefp(&vrs[vd], &vrs[vb]);
+                       break;
+               case 5: /* vrsqrtefp */
+                       vrsqrtefp(&vrs[vd], &vrs[vb]);
+                       break;
+               case 6: /* vexptefp */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = eexp2(vrs[vb].u[i]);
+                       break;
+               case 7: /* vlogefp */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = elog2(vrs[vb].u[i]);
+                       break;
+               case 8:         /* vrfin */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = rfin(vrs[vb].u[i]);
+                       break;
+               case 9:         /* vrfiz */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = rfiz(vrs[vb].u[i]);
+                       break;
+               case 10:        /* vrfip */
+                       for (i = 0; i < 4; ++i) {
+                               u32 x = vrs[vb].u[i];
+                               x = (x & 0x80000000)? rfiz(x): rfii(x);
+                               vrs[vd].u[i] = x;
+                       }
+                       break;
+               case 11:        /* vrfim */
+                       for (i = 0; i < 4; ++i) {
+                               u32 x = vrs[vb].u[i];
+                               x = (x & 0x80000000)? rfii(x): rfiz(x);
+                               vrs[vd].u[i] = x;
+                       }
+                       break;
+               case 14:        /* vctuxs */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va,
+                                       &current->thread.vr_state.vscr.u[3]);
+                       break;
+               case 15:        /* vctsxs */
+                       for (i = 0; i < 4; ++i)
+                               vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va,
+                                       &current->thread.vr_state.vscr.u[3]);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case 46:        /* vmaddfp */
+               vmaddfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
+               break;
+       case 47:        /* vnmsubfp */
+               vnmsubfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}