]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/x86/kvm/emulate.c
KVM: x86: Wrong flags on CMPS and SCAS emulation
[karo-tx-linux.git] / arch / x86 / kvm / emulate.c
index 749f9fa3825422adb9a3cc9a5867f52ec256dedd..7aba1db168c9c2dd8453d00abdf93845a1d79eed 100644 (file)
 #define CheckPerm   ((u64)1 << 49)  /* Has valid check_perm field */
 #define NoBigReal   ((u64)1 << 50)  /* No big real mode */
 #define PrivUD      ((u64)1 << 51)  /* #UD instead of #GP on CPL > 0 */
+#define NearBranch  ((u64)1 << 52)  /* Near branches */
 
 #define DstXacc     (DstAccLo | SrcAccHi | SrcWrite)
 
@@ -379,6 +380,15 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
        ON64(FOP2E(op##q, rax, cl)) \
        FOP_END
 
+/* 2 operand, src and dest are reversed */
+#define FASTOP2R(op, name) \
+       FOP_START(name) \
+       FOP2E(op##b, dl, al) \
+       FOP2E(op##w, dx, ax) \
+       FOP2E(op##l, edx, eax) \
+       ON64(FOP2E(op##q, rdx, rax)) \
+       FOP_END
+
 #define FOP3E(op,  dst, src, src2) \
        FOP_ALIGN #op " %" #src2 ", %" #src ", %" #dst " \n\t" FOP_RET
 
@@ -574,12 +584,14 @@ static inline int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
        case 4:
                ctxt->_eip = (u32)dst;
                break;
+#ifdef CONFIG_X86_64
        case 8:
                if ((cs_l && is_noncanonical_address(dst)) ||
-                   (!cs_l && (dst & ~(u32)-1)))
+                   (!cs_l && (dst >> 32) != 0))
                        return emulate_gp(ctxt, 0);
                ctxt->_eip = dst;
                break;
+#endif
        default:
                WARN(1, "unsupported eip assignment size\n");
        }
@@ -639,10 +651,11 @@ static bool insn_aligned(struct x86_emulate_ctxt *ctxt, unsigned size)
                return true;
 }
 
-static int __linearize(struct x86_emulate_ctxt *ctxt,
-                    struct segmented_address addr,
-                    unsigned size, bool write, bool fetch,
-                    ulong *linear)
+static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
+                                      struct segmented_address addr,
+                                      unsigned *max_size, unsigned size,
+                                      bool write, bool fetch,
+                                      ulong *linear)
 {
        struct desc_struct desc;
        bool usable;
@@ -651,11 +664,17 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
        u16 sel;
        unsigned cpl;
 
-       la = seg_base(ctxt, addr.seg) + addr.ea;
+       la = seg_base(ctxt, addr.seg) +
+           (fetch || ctxt->ad_bytes == 8 ? addr.ea : (u32)addr.ea);
+       *max_size = 0;
        switch (ctxt->mode) {
        case X86EMUL_MODE_PROT64:
-               if (((signed long)la << 16) >> 16 != la)
+               if (is_noncanonical_address(la))
                        return emulate_gp(ctxt, 0);
+
+               *max_size = min_t(u64, ~0u, (1ull << 48) - la);
+               if (size > *max_size)
+                       goto bad;
                break;
        default:
                usable = ctxt->ops->get_segment(ctxt, &sel, &desc, NULL,
@@ -673,23 +692,28 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
                if ((ctxt->mode == X86EMUL_MODE_REAL) && !fetch &&
                    (ctxt->d & NoBigReal)) {
                        /* la is between zero and 0xffff */
-                       if (la > 0xffff || (u32)(la + size - 1) > 0xffff)
+                       if (la > 0xffff)
                                goto bad;
+                       *max_size = 0x10000 - la;
                } else if ((desc.type & 8) || !(desc.type & 4)) {
                        /* expand-up segment */
-                       if (addr.ea > lim || (u32)(addr.ea + size - 1) > lim)
+                       if (addr.ea > lim)
                                goto bad;
+                       *max_size = min_t(u64, ~0u, (u64)lim + 1 - addr.ea);
                } else {
                        /* expand-down segment */
-                       if (addr.ea <= lim || (u32)(addr.ea + size - 1) <= lim)
+                       if (addr.ea <= lim)
                                goto bad;
                        lim = desc.d ? 0xffffffff : 0xffff;
-                       if (addr.ea > lim || (u32)(addr.ea + size - 1) > lim)
+                       if (addr.ea > lim)
                                goto bad;
+                       *max_size = min_t(u64, ~0u, (u64)lim + 1 - addr.ea);
                }
+               if (size > *max_size)
+                       goto bad;
                cpl = ctxt->ops->cpl(ctxt);
-               if (!(desc.type & 8)) {
-                       /* data segment */
+               if (!fetch) {
+                       /* data segment or readable code segment */
                        if (cpl > desc.dpl)
                                goto bad;
                } else if ((desc.type & 8) && !(desc.type & 4)) {
@@ -703,7 +727,7 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
                }
                break;
        }
-       if (fetch ? ctxt->mode != X86EMUL_MODE_PROT64 : ctxt->ad_bytes != 8)
+       if (ctxt->mode != X86EMUL_MODE_PROT64)
                la &= (u32)-1;
        if (insn_aligned(ctxt, size) && ((la & (size - 1)) != 0))
                return emulate_gp(ctxt, 0);
@@ -711,9 +735,9 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
        return X86EMUL_CONTINUE;
 bad:
        if (addr.seg == VCPU_SREG_SS)
-               return emulate_ss(ctxt, sel);
+               return emulate_ss(ctxt, 0);
        else
-               return emulate_gp(ctxt, sel);
+               return emulate_gp(ctxt, 0);
 }
 
 static int linearize(struct x86_emulate_ctxt *ctxt,
@@ -721,7 +745,8 @@ static int linearize(struct x86_emulate_ctxt *ctxt,
                     unsigned size, bool write,
                     ulong *linear)
 {
-       return __linearize(ctxt, addr, size, write, false, linear);
+       unsigned max_size;
+       return __linearize(ctxt, addr, &max_size, size, write, false, linear);
 }
 
 
@@ -746,17 +771,27 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt,
 static int __do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt, int op_size)
 {
        int rc;
-       unsigned size;
+       unsigned size, max_size;
        unsigned long linear;
        int cur_size = ctxt->fetch.end - ctxt->fetch.data;
        struct segmented_address addr = { .seg = VCPU_SREG_CS,
                                           .ea = ctxt->eip + cur_size };
 
-       size = 15UL ^ cur_size;
-       rc = __linearize(ctxt, addr, size, false, true, &linear);
+       /*
+        * We do not know exactly how many bytes will be needed, and
+        * __linearize is expensive, so fetch as much as possible.  We
+        * just have to avoid going beyond the 15 byte limit, the end
+        * of the segment, or the end of the page.
+        *
+        * __linearize is called with size 0 so that it does not do any
+        * boundary check itself.  Instead, we use max_size to check
+        * against op_size.
+        */
+       rc = __linearize(ctxt, addr, &max_size, 0, false, true, &linear);
        if (unlikely(rc != X86EMUL_CONTINUE))
                return rc;
 
+       size = min_t(unsigned, 15UL ^ cur_size, max_size);
        size = min_t(unsigned, size, PAGE_SIZE - offset_in_page(linear));
 
        /*
@@ -766,7 +801,8 @@ static int __do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt, int op_size)
         * still, we must have hit the 15-byte boundary.
         */
        if (unlikely(size < op_size))
-               return X86EMUL_UNHANDLEABLE;
+               return emulate_gp(ctxt, 0);
+
        rc = ctxt->ops->fetch(ctxt, linear, ctxt->fetch.end,
                              size, &ctxt->exception);
        if (unlikely(rc != X86EMUL_CONTINUE))
@@ -886,6 +922,8 @@ FASTOP2W(btc);
 
 FASTOP2(xadd);
 
+FASTOP2R(cmp, cmp_r);
+
 static u8 test_cc(unsigned int condition, unsigned long flags)
 {
        u8 rc;
@@ -1196,6 +1234,7 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
                        if (index_reg != 4)
                                modrm_ea += reg_read(ctxt, index_reg) << scale;
                } else if ((ctxt->modrm_rm & 7) == 5 && ctxt->modrm_mod == 0) {
+                       modrm_ea += insn_fetch(s32, ctxt);
                        if (ctxt->mode == X86EMUL_MODE_PROT64)
                                ctxt->rip_relative = 1;
                } else {
@@ -1204,10 +1243,6 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
                        adjust_modrm_seg(ctxt, base_reg);
                }
                switch (ctxt->modrm_mod) {
-               case 0:
-                       if (ctxt->modrm_rm == 5)
-                               modrm_ea += insn_fetch(s32, ctxt);
-                       break;
                case 1:
                        modrm_ea += insn_fetch(s8, ctxt);
                        break;
@@ -2012,7 +2047,7 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
 
        rc = assign_eip_far(ctxt, ctxt->src.val, new_desc.l);
        if (rc != X86EMUL_CONTINUE) {
-               WARN_ON(!ctxt->mode != X86EMUL_MODE_PROT64);
+               WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64);
                /* assigning eip failed; restore the old cs */
                ops->set_segment(ctxt, old_sel, &old_desc, 0, VCPU_SREG_CS);
                return rc;
@@ -2020,31 +2055,22 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
        return rc;
 }
 
-static int em_grp45(struct x86_emulate_ctxt *ctxt)
+static int em_jmp_abs(struct x86_emulate_ctxt *ctxt)
 {
-       int rc = X86EMUL_CONTINUE;
+       return assign_eip_near(ctxt, ctxt->src.val);
+}
 
-       switch (ctxt->modrm_reg) {
-       case 2: /* call near abs */ {
-               long int old_eip;
-               old_eip = ctxt->_eip;
-               rc = assign_eip_near(ctxt, ctxt->src.val);
-               if (rc != X86EMUL_CONTINUE)
-                       break;
-               ctxt->src.val = old_eip;
-               rc = em_push(ctxt);
-               break;
-       }
-       case 4: /* jmp abs */
-               rc = assign_eip_near(ctxt, ctxt->src.val);
-               break;
-       case 5: /* jmp far */
-               rc = em_jmp_far(ctxt);
-               break;
-       case 6: /* push */
-               rc = em_push(ctxt);
-               break;
-       }
+static int em_call_near_abs(struct x86_emulate_ctxt *ctxt)
+{
+       int rc;
+       long int old_eip;
+
+       old_eip = ctxt->_eip;
+       rc = assign_eip_near(ctxt, ctxt->src.val);
+       if (rc != X86EMUL_CONTINUE)
+               return rc;
+       ctxt->src.val = old_eip;
+       rc = em_push(ctxt);
        return rc;
 }
 
@@ -2109,7 +2135,7 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
                return rc;
        rc = assign_eip_far(ctxt, eip, new_desc.l);
        if (rc != X86EMUL_CONTINUE) {
-               WARN_ON(!ctxt->mode != X86EMUL_MODE_PROT64);
+               WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64);
                ops->set_segment(ctxt, old_cs, &old_desc, 0, VCPU_SREG_CS);
        }
        return rc;
@@ -2291,6 +2317,7 @@ static int em_syscall(struct x86_emulate_ctxt *ctxt)
 
                ops->get_msr(ctxt, MSR_SYSCALL_MASK, &msr_data);
                ctxt->eflags &= ~msr_data;
+               ctxt->eflags |= EFLG_RESERVED_ONE_MASK;
 #endif
        } else {
                /* legacy mode */
@@ -2400,6 +2427,8 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt)
                if ((msr_data & 0xfffc) == 0x0)
                        return emulate_gp(ctxt, 0);
                ss_sel = (u16)(msr_data + 24);
+               rcx = (u32)rcx;
+               rdx = (u32)rdx;
                break;
        case X86EMUL_MODE_PROT64:
                cs_sel = (u16)(msr_data + 32);
@@ -3190,6 +3219,8 @@ static int em_mov_rm_sreg(struct x86_emulate_ctxt *ctxt)
                return emulate_ud(ctxt);
 
        ctxt->dst.val = get_segment_selector(ctxt, ctxt->modrm_reg);
+       if (ctxt->dst.bytes == 4 && ctxt->dst.type == OP_MEM)
+               ctxt->dst.bytes = 2;
        return X86EMUL_CONTINUE;
 }
 
@@ -3571,8 +3602,15 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt)
        if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5))
                return emulate_ud(ctxt);
 
-       if (check_dr7_gd(ctxt))
+       if (check_dr7_gd(ctxt)) {
+               ulong dr6;
+
+               ctxt->ops->get_dr(ctxt, 6, &dr6);
+               dr6 &= ~15;
+               dr6 |= DR6_BD | DR6_RTM;
+               ctxt->ops->set_dr(ctxt, 6, dr6);
                return emulate_db(ctxt);
+       }
 
        return X86EMUL_CONTINUE;
 }
@@ -3755,11 +3793,11 @@ static const struct opcode group4[] = {
 static const struct opcode group5[] = {
        F(DstMem | SrcNone | Lock,              em_inc),
        F(DstMem | SrcNone | Lock,              em_dec),
-       I(SrcMem | Stack,                       em_grp45),
+       I(SrcMem | NearBranch,                  em_call_near_abs),
        I(SrcMemFAddr | ImplicitOps | Stack,    em_call_far),
-       I(SrcMem | Stack,                       em_grp45),
-       I(SrcMemFAddr | ImplicitOps,            em_grp45),
-       I(SrcMem | Stack,                       em_grp45), D(Undefined),
+       I(SrcMem | NearBranch,                  em_jmp_abs),
+       I(SrcMemFAddr | ImplicitOps,            em_jmp_far),
+       I(SrcMem | Stack,                       em_push), D(Undefined),
 };
 
 static const struct opcode group6[] = {
@@ -3939,7 +3977,7 @@ static const struct opcode opcode_table[256] = {
        I2bvIP(DstDI | SrcDX | Mov | String | Unaligned, em_in, ins, check_perm_in), /* insb, insw/insd */
        I2bvIP(SrcSI | DstDX | String, em_out, outs, check_perm_out), /* outsb, outsw/outsd */
        /* 0x70 - 0x7F */
-       X16(D(SrcImmByte)),
+       X16(D(SrcImmByte | NearBranch)),
        /* 0x80 - 0x87 */
        G(ByteOp | DstMem | SrcImm, group1),
        G(DstMem | SrcImm, group1),
@@ -3966,20 +4004,20 @@ static const struct opcode opcode_table[256] = {
        I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov),
        I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov),
        I2bv(SrcSI | DstDI | Mov | String, em_mov),
-       F2bv(SrcSI | DstDI | String | NoWrite, em_cmp),
+       F2bv(SrcSI | DstDI | String | NoWrite, em_cmp_r),
        /* 0xA8 - 0xAF */
        F2bv(DstAcc | SrcImm | NoWrite, em_test),
        I2bv(SrcAcc | DstDI | Mov | String, em_mov),
        I2bv(SrcSI | DstAcc | Mov | String, em_mov),
-       F2bv(SrcAcc | DstDI | String | NoWrite, em_cmp),
+       F2bv(SrcAcc | DstDI | String | NoWrite, em_cmp_r),
        /* 0xB0 - 0xB7 */
        X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)),
        /* 0xB8 - 0xBF */
        X8(I(DstReg | SrcImm64 | Mov, em_mov)),
        /* 0xC0 - 0xC7 */
        G(ByteOp | Src2ImmByte, group2), G(Src2ImmByte, group2),
-       I(ImplicitOps | Stack | SrcImmU16, em_ret_near_imm),
-       I(ImplicitOps | Stack, em_ret),
+       I(ImplicitOps | NearBranch | SrcImmU16, em_ret_near_imm),
+       I(ImplicitOps | NearBranch, em_ret),
        I(DstReg | SrcMemFAddr | ModRM | No64 | Src2ES, em_lseg),
        I(DstReg | SrcMemFAddr | ModRM | No64 | Src2DS, em_lseg),
        G(ByteOp, group11), G(0, group11),
@@ -3999,13 +4037,14 @@ static const struct opcode opcode_table[256] = {
        /* 0xD8 - 0xDF */
        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),
+       X3(I(SrcImmByte | NearBranch, em_loop)),
+       I(SrcImmByte | NearBranch, em_jcxz),
        I2bvIP(SrcImmUByte | DstAcc, em_in,  in,  check_perm_in),
        I2bvIP(SrcAcc | DstImmUByte, em_out, out, check_perm_out),
        /* 0xE8 - 0xEF */
-       I(SrcImm | Stack, em_call), D(SrcImm | ImplicitOps),
-       I(SrcImmFAddr | No64, em_jmp_far), D(SrcImmByte | ImplicitOps),
+       I(SrcImm | NearBranch, em_call), D(SrcImm | ImplicitOps | NearBranch),
+       I(SrcImmFAddr | No64, em_jmp_far),
+       D(SrcImmByte | ImplicitOps | NearBranch),
        I2bvIP(SrcDX | DstAcc, em_in,  in,  check_perm_in),
        I2bvIP(SrcAcc | DstDX, em_out, out, check_perm_out),
        /* 0xF0 - 0xF7 */
@@ -4065,7 +4104,7 @@ static const struct opcode twobyte_table[256] = {
        N, N, N, N,
        N, N, N, GP(SrcReg | DstMem | ModRM | Mov, &pfx_0f_6f_0f_7f),
        /* 0x80 - 0x8F */
-       X16(D(SrcImm)),
+       X16(D(SrcImm | NearBranch)),
        /* 0x90 - 0x9F */
        X16(D(ByteOp | DstMem | SrcNone | ModRM| Mov)),
        /* 0xA0 - 0xA7 */
@@ -4534,7 +4573,7 @@ done_prefixes:
                return EMULATION_FAILED;
 
        if (unlikely(ctxt->d &
-                    (NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm))) {
+           (NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm|NearBranch))) {
                /*
                 * These are copied unconditionally here, and checked unconditionally
                 * in x86_emulate_insn.
@@ -4545,8 +4584,12 @@ done_prefixes:
                if (ctxt->d & NotImpl)
                        return EMULATION_FAILED;
 
-               if (mode == X86EMUL_MODE_PROT64 && (ctxt->d & Stack))
-                       ctxt->op_bytes = 8;
+               if (mode == X86EMUL_MODE_PROT64) {
+                       if (ctxt->op_bytes == 4 && (ctxt->d & Stack))
+                               ctxt->op_bytes = 8;
+                       else if (ctxt->d & NearBranch)
+                               ctxt->op_bytes = 8;
+               }
 
                if (ctxt->d & Op3264) {
                        if (mode == X86EMUL_MODE_PROT64)