]> Pileus Git - ~andy/linux/blobdiff - arch/x86/kvm/emulate.c
KVM: x86 emulator: Support for declaring single operand fastops
[~andy/linux] / arch / x86 / kvm / emulate.c
index c7547b3fd527ec468911ec3887824a72227480e0..42c53c8071bee2bd834768e191fdb8d3a42e1f89 100644 (file)
@@ -24,6 +24,7 @@
 #include "kvm_cache_regs.h"
 #include <linux/module.h>
 #include <asm/kvm_emulate.h>
+#include <linux/stringify.h>
 
 #include "x86.h"
 #include "tss.h"
 #define GroupDual   (2<<15)     /* Alternate decoding of mod == 3 */
 #define Prefix      (3<<15)     /* Instruction varies with 66/f2/f3 prefix */
 #define RMExt       (4<<15)     /* Opcode extension in ModRM r/m if mod == 3 */
+#define Escape      (5<<15)     /* Escape to coprocessor instruction */
 #define Sse         (1<<18)     /* SSE Vector instruction */
 /* Generic ModRM decode. */
 #define ModRM       (1<<19)
 #define Aligned     ((u64)1 << 41)  /* Explicitly aligned (e.g. MOVDQA) */
 #define Unaligned   ((u64)1 << 42)  /* Explicitly unaligned (e.g. MOVDQU) */
 #define Avx         ((u64)1 << 43)  /* Advanced Vector Extensions */
+#define Fastop      ((u64)1 << 44)  /* Use opcode::u.fastop */
 
 #define X2(x...) x, x
 #define X3(x...) X2(x), x
 #define X8(x...) X4(x), X4(x)
 #define X16(x...) X8(x), X8(x)
 
+#define NR_FASTOP (ilog2(sizeof(ulong)) + 1)
+#define FASTOP_SIZE 8
+
+/*
+ * fastop functions have a special calling convention:
+ *
+ * dst:    [rdx]:rax  (in/out)
+ * src:    rbx        (in/out)
+ * src2:   rcx        (in)
+ * flags:  rflags     (in/out)
+ *
+ * Moreover, they are all exactly FASTOP_SIZE bytes long, so functions for
+ * different operand sizes can be reached by calculation, rather than a jump
+ * table (which would be bigger than the code).
+ *
+ * fastop functions are declared as taking a never-defined fastop parameter,
+ * so they can't be called from C directly.
+ */
+
+struct fastop;
+
 struct opcode {
        u64 flags : 56;
        u64 intercept : 8;
@@ -166,6 +190,8 @@ struct opcode {
                const struct opcode *group;
                const struct group_dual *gdual;
                const struct gprefix *gprefix;
+               const struct escape *esc;
+               void (*fastop)(struct fastop *fake);
        } u;
        int (*check_perm)(struct x86_emulate_ctxt *ctxt);
 };
@@ -182,6 +208,11 @@ struct gprefix {
        struct opcode pfx_f3;
 };
 
+struct escape {
+       struct opcode op[8];
+       struct opcode high[64];
+};
+
 /* EFLAGS bit definitions. */
 #define EFLG_ID (1<<21)
 #define EFLG_VIP (1<<20)
@@ -409,6 +440,30 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt)
                }                                                       \
        } while (0)
 
+#define FOP_ALIGN ".align " __stringify(FASTOP_SIZE) " \n\t"
+#define FOP_RET   "ret \n\t"
+
+#define FOP_START(op) \
+       extern void em_##op(struct fastop *fake); \
+       asm(".pushsection .text, \"ax\" \n\t" \
+           ".global em_" #op " \n\t" \
+            FOP_ALIGN \
+           "em_" #op ": \n\t"
+
+#define FOP_END \
+           ".popsection")
+
+#define FOP1E(op,  dst) \
+       FOP_ALIGN #op " %" #dst " \n\t" FOP_RET
+
+#define FASTOP1(op) \
+       FOP_START(op) \
+       FOP1E(op##b, al) \
+       FOP1E(op##w, ax) \
+       FOP1E(op##l, eax) \
+       ON64(FOP1E(op##q, rax)) \
+       FOP_END
+
 #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex)                 \
        do {                                                            \
                unsigned long _tmp;                                     \
@@ -665,7 +720,7 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
        ulong la;
        u32 lim;
        u16 sel;
-       unsigned cpl, rpl;
+       unsigned cpl;
 
        la = seg_base(ctxt, addr.seg) + addr.ea;
        switch (ctxt->mode) {
@@ -699,11 +754,6 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
                                goto bad;
                }
                cpl = ctxt->ops->cpl(ctxt);
-               if (ctxt->mode == X86EMUL_MODE_REAL)
-                       rpl = 0;
-               else
-                       rpl = sel & 3;
-               cpl = max(cpl, rpl);
                if (!(desc.type & 8)) {
                        /* data segment */
                        if (cpl > desc.dpl)
@@ -996,6 +1046,53 @@ static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
        ctxt->ops->put_fpu(ctxt);
 }
 
+static int em_fninit(struct x86_emulate_ctxt *ctxt)
+{
+       if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
+               return emulate_nm(ctxt);
+
+       ctxt->ops->get_fpu(ctxt);
+       asm volatile("fninit");
+       ctxt->ops->put_fpu(ctxt);
+       return X86EMUL_CONTINUE;
+}
+
+static int em_fnstcw(struct x86_emulate_ctxt *ctxt)
+{
+       u16 fcw;
+
+       if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
+               return emulate_nm(ctxt);
+
+       ctxt->ops->get_fpu(ctxt);
+       asm volatile("fnstcw %0": "+m"(fcw));
+       ctxt->ops->put_fpu(ctxt);
+
+       /* force 2 byte destination */
+       ctxt->dst.bytes = 2;
+       ctxt->dst.val = fcw;
+
+       return X86EMUL_CONTINUE;
+}
+
+static int em_fnstsw(struct x86_emulate_ctxt *ctxt)
+{
+       u16 fsw;
+
+       if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
+               return emulate_nm(ctxt);
+
+       ctxt->ops->get_fpu(ctxt);
+       asm volatile("fnstsw %0": "+m"(fsw));
+       ctxt->ops->put_fpu(ctxt);
+
+       /* force 2 byte destination */
+       ctxt->dst.bytes = 2;
+       ctxt->dst.val = fsw;
+
+       return X86EMUL_CONTINUE;
+}
+
 static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
                                    struct operand *op)
 {
@@ -3595,7 +3692,9 @@ static int check_perm_out(struct x86_emulate_ctxt *ctxt)
 #define EXT(_f, _e) { .flags = ((_f) | RMExt), .u.group = (_e) }
 #define G(_f, _g) { .flags = ((_f) | Group | ModRM), .u.group = (_g) }
 #define GD(_f, _g) { .flags = ((_f) | GroupDual | ModRM), .u.gdual = (_g) }
+#define E(_f, _e) { .flags = ((_f) | Escape | ModRM), .u.esc = (_e) }
 #define I(_f, _e) { .flags = (_f), .u.execute = (_e) }
+#define F(_f, _e) { .flags = (_f) | Fastop, .u.fastop = (_e) }
 #define II(_f, _e, _i) \
        { .flags = (_f), .u.execute = (_e), .intercept = x86_intercept_##_i }
 #define IIP(_f, _e, _i, _p) \
@@ -3730,6 +3829,69 @@ static const struct gprefix pfx_vmovntpx = {
        I(0, em_mov), N, N, N,
 };
 
+static const struct escape escape_d9 = { {
+       N, N, N, N, N, N, N, I(DstMem, em_fnstcw),
+}, {
+       /* 0xC0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xC8 - 0xCF */
+       N, N, N, N, N, N, N, N,
+       /* 0xD0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xD8 - 0xDF */
+       N, N, N, N, N, N, N, N,
+       /* 0xE0 - 0xE7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xE8 - 0xEF */
+       N, N, N, N, N, N, N, N,
+       /* 0xF0 - 0xF7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xF8 - 0xFF */
+       N, N, N, N, N, N, N, N,
+} };
+
+static const struct escape escape_db = { {
+       N, N, N, N, N, N, N, N,
+}, {
+       /* 0xC0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xC8 - 0xCF */
+       N, N, N, N, N, N, N, N,
+       /* 0xD0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xD8 - 0xDF */
+       N, N, N, N, N, N, N, N,
+       /* 0xE0 - 0xE7 */
+       N, N, N, I(ImplicitOps, em_fninit), N, N, N, N,
+       /* 0xE8 - 0xEF */
+       N, N, N, N, N, N, N, N,
+       /* 0xF0 - 0xF7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xF8 - 0xFF */
+       N, N, N, N, N, N, N, N,
+} };
+
+static const struct escape escape_dd = { {
+       N, N, N, N, N, N, N, I(DstMem, em_fnstsw),
+}, {
+       /* 0xC0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xC8 - 0xCF */
+       N, N, N, N, N, N, N, N,
+       /* 0xD0 - 0xC7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xD8 - 0xDF */
+       N, N, N, N, N, N, N, N,
+       /* 0xE0 - 0xE7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xE8 - 0xEF */
+       N, N, N, N, N, N, N, N,
+       /* 0xF0 - 0xF7 */
+       N, N, N, N, N, N, N, N,
+       /* 0xF8 - 0xFF */
+       N, N, N, N, N, N, N, N,
+} };
+
 static const struct opcode opcode_table[256] = {
        /* 0x00 - 0x07 */
        I6ALU(Lock, em_add),
@@ -3826,7 +3988,7 @@ static const struct opcode opcode_table[256] = {
        D2bv(DstMem | SrcOne | ModRM), D2bv(DstMem | ModRM),
        N, I(DstAcc | SrcImmByte | No64, em_aad), N, N,
        /* 0xD8 - 0xDF */
-       N, N, N, N, N, N, N, N,
+       N, E(0, &escape_d9), N, E(0, &escape_db), N, E(0, &escape_dd), N, N,
        /* 0xE0 - 0xE7 */
        X3(I(SrcImmByte, em_loop)),
        I(SrcImmByte, em_jcxz),
@@ -4251,6 +4413,12 @@ done_prefixes:
                        case 0xf3: opcode = opcode.u.gprefix->pfx_f3; break;
                        }
                        break;
+               case Escape:
+                       if (ctxt->modrm > 0xbf)
+                               opcode = opcode.u.esc->high[ctxt->modrm - 0xc0];
+                       else
+                               opcode = opcode.u.esc->op[(ctxt->modrm >> 3) & 7];
+                       break;
                default:
                        return EMULATION_FAILED;
                }
@@ -4383,6 +4551,16 @@ static void fetch_possible_mmx_operand(struct x86_emulate_ctxt *ctxt,
                read_mmx_reg(ctxt, &op->mm_val, op->addr.mm);
 }
 
+static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *))
+{
+       ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF;
+       fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE;
+       asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n"
+           : "+a"(ctxt->dst.val), "+b"(ctxt->src.val), [flags]"+D"(flags)
+       : "c"(ctxt->src2.val), [fastop]"S"(fop));
+       ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK);
+       return X86EMUL_CONTINUE;
+}
 
 int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 {
@@ -4512,6 +4690,13 @@ special_insn:
        }
 
        if (ctxt->execute) {
+               if (ctxt->d & Fastop) {
+                       void (*fop)(struct fastop *) = (void *)ctxt->execute;
+                       rc = fastop(ctxt, fop);
+                       if (rc != X86EMUL_CONTINUE)
+                               goto done;
+                       goto writeback;
+               }
                rc = ctxt->execute(ctxt);
                if (rc != X86EMUL_CONTINUE)
                        goto done;