]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/x86/kvm/emulate.c
KVM: x86: Return UNHANDLABLE on unsupported SYSENTER
[karo-tx-linux.git] / arch / x86 / kvm / emulate.c
index 147f3fdfb5ced254149a008af3bdeaeff877eb27..dab9b74c29de9fec4cd305739458ab1a6419c0d6 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
 
@@ -654,7 +664,8 @@ static __always_inline 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:
@@ -701,8 +712,8 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
                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)) {
@@ -716,7 +727,7 @@ static __always_inline 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);
@@ -911,6 +922,8 @@ FASTOP2W(btc);
 
 FASTOP2(xadd);
 
+FASTOP2R(cmp, cmp_r);
+
 static u8 test_cc(unsigned int condition, unsigned long flags)
 {
        u8 rc;
@@ -1221,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 {
@@ -1229,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;
@@ -1610,6 +1620,9 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
                                sizeof(base3), &ctxt->exception);
                if (ret != X86EMUL_CONTINUE)
                        return ret;
+               if (is_noncanonical_address(get_desc_base(&seg_desc) |
+                                            ((u64)base3 << 32)))
+                       return emulate_gp(ctxt, 0);
        }
 load:
        ctxt->ops->set_segment(ctxt, selector, &seg_desc, base3, seg);
@@ -1807,6 +1820,10 @@ static int em_push_sreg(struct x86_emulate_ctxt *ctxt)
        int seg = ctxt->src2.val;
 
        ctxt->src.val = get_segment_selector(ctxt, seg);
+       if (ctxt->op_bytes == 4) {
+               rsp_increment(ctxt, -2);
+               ctxt->op_bytes = 2;
+       }
 
        return em_push(ctxt);
 }
@@ -2307,6 +2324,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 */
@@ -2340,11 +2358,9 @@ static int em_sysenter(struct x86_emulate_ctxt *ctxt)
            && !vendor_intel(ctxt))
                return emulate_ud(ctxt);
 
-       /* XXX sysenter/sysexit have not been tested in 64bit mode.
-       * Therefore, we inject an #UD.
-       */
+       /* sysenter/sysexit have not been tested in 64bit mode. */
        if (ctxt->mode == X86EMUL_MODE_PROT64)
-               return emulate_ud(ctxt);
+               return X86EMUL_UNHANDLEABLE;
 
        setup_syscalls_segments(ctxt, &cs, &ss);
 
@@ -2416,6 +2432,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);
@@ -2804,7 +2822,8 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
         *
         * 1. jmp/call/int to task gate: Check against DPL of the task gate
         * 2. Exception/IRQ/iret: No check is performed
-        * 3. jmp/call to TSS: Check against DPL of the TSS
+        * 3. jmp/call to TSS/task-gate: No check is performed since the
+        *    hardware checks it before exiting.
         */
        if (reason == TASK_SWITCH_GATE) {
                if (idt_index != -1) {
@@ -2821,13 +2840,8 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
                        if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl)
                                return emulate_gp(ctxt, (idt_index << 3) | 0x2);
                }
-       } else if (reason != TASK_SWITCH_IRET) {
-               int dpl = next_tss_desc.dpl;
-               if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl)
-                       return emulate_gp(ctxt, tss_selector);
        }
 
-
        desc_limit = desc_limit_scaled(&next_tss_desc);
        if (!next_tss_desc.p ||
            ((desc_limit < 0x67 && (next_tss_desc.type & 8)) ||
@@ -3206,6 +3220,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;
 }
 
@@ -3308,7 +3324,7 @@ static int em_sidt(struct x86_emulate_ctxt *ctxt)
        return emulate_store_desc_ptr(ctxt, ctxt->ops->get_idt);
 }
 
-static int em_lgdt(struct x86_emulate_ctxt *ctxt)
+static int em_lgdt_lidt(struct x86_emulate_ctxt *ctxt, bool lgdt)
 {
        struct desc_ptr desc_ptr;
        int rc;
@@ -3320,12 +3336,23 @@ static int em_lgdt(struct x86_emulate_ctxt *ctxt)
                             ctxt->op_bytes);
        if (rc != X86EMUL_CONTINUE)
                return rc;
-       ctxt->ops->set_gdt(ctxt, &desc_ptr);
+       if (ctxt->mode == X86EMUL_MODE_PROT64 &&
+           is_noncanonical_address(desc_ptr.address))
+               return emulate_gp(ctxt, 0);
+       if (lgdt)
+               ctxt->ops->set_gdt(ctxt, &desc_ptr);
+       else
+               ctxt->ops->set_idt(ctxt, &desc_ptr);
        /* Disable writeback. */
        ctxt->dst.type = OP_NONE;
        return X86EMUL_CONTINUE;
 }
 
+static int em_lgdt(struct x86_emulate_ctxt *ctxt)
+{
+       return em_lgdt_lidt(ctxt, true);
+}
+
 static int em_vmmcall(struct x86_emulate_ctxt *ctxt)
 {
        int rc;
@@ -3339,20 +3366,7 @@ static int em_vmmcall(struct x86_emulate_ctxt *ctxt)
 
 static int em_lidt(struct x86_emulate_ctxt *ctxt)
 {
-       struct desc_ptr desc_ptr;
-       int rc;
-
-       if (ctxt->mode == X86EMUL_MODE_PROT64)
-               ctxt->op_bytes = 8;
-       rc = read_descriptor(ctxt, ctxt->src.addr.mem,
-                            &desc_ptr.size, &desc_ptr.address,
-                            ctxt->op_bytes);
-       if (rc != X86EMUL_CONTINUE)
-               return rc;
-       ctxt->ops->set_idt(ctxt, &desc_ptr);
-       /* Disable writeback. */
-       ctxt->dst.type = OP_NONE;
-       return X86EMUL_CONTINUE;
+       return em_lgdt_lidt(ctxt, false);
 }
 
 static int em_smsw(struct x86_emulate_ctxt *ctxt)
@@ -3545,7 +3559,7 @@ static int check_cr_write(struct x86_emulate_ctxt *ctxt)
 
                ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
                if (efer & EFER_LMA)
-                       rsvd = CR3_L_MODE_RESERVED_BITS;
+                       rsvd = CR3_L_MODE_RESERVED_BITS & ~CR3_PCID_INVD;
 
                if (new_val & rsvd)
                        return emulate_gp(ctxt, 0);
@@ -3587,8 +3601,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;
 }
@@ -3771,9 +3792,9 @@ 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_call_near_abs),
+       I(SrcMem | NearBranch,                  em_call_near_abs),
        I(SrcMemFAddr | ImplicitOps | Stack,    em_call_far),
-       I(SrcMem | Stack,                       em_jmp_abs),
+       I(SrcMem | NearBranch,                  em_jmp_abs),
        I(SrcMemFAddr | ImplicitOps,            em_jmp_far),
        I(SrcMem | Stack,                       em_push), D(Undefined),
 };
@@ -3955,7 +3976,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),
@@ -3982,20 +4003,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),
@@ -4015,13 +4036,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 */
@@ -4081,7 +4103,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 */
@@ -4493,8 +4515,7 @@ done_prefixes:
 
        /* vex-prefix instructions are not implemented */
        if (ctxt->opcode_len == 1 && (ctxt->b == 0xc5 || ctxt->b == 0xc4) &&
-           (mode == X86EMUL_MODE_PROT64 ||
-           (mode >= X86EMUL_MODE_PROT16 && (ctxt->modrm & 0x80)))) {
+           (mode == X86EMUL_MODE_PROT64 || (ctxt->modrm & 0xc0) == 0xc0)) {
                ctxt->d = NotImpl;
        }
 
@@ -4550,7 +4571,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.
@@ -4561,8 +4582,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)