KVM: x86: emulator: introduce emulator_recalc_and_set_mode
authorMaxim Levitsky <mlevitsk@redhat.com>
Tue, 25 Oct 2022 12:47:29 +0000 (15:47 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 Nov 2022 14:47:24 +0000 (15:47 +0100)
commit d087e0f79fa0dd336a9a6b2f79ec23120f5eff73 upstream.

Some instructions update the cpu execution mode, which needs to update the
emulation mode.

Extract this code, and make assign_eip_far use it.

assign_eip_far now reads CS, instead of getting it via a parameter,
which is ok, because callers always assign CS to the same value
before calling this function.

No functional change is intended.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20221025124741.228045-12-mlevitsk@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/kvm/emulate.c

index 1a01aa205ff01ced4f3b12a13f93452c5949de30..271fe269a92f015bef9c45c5002f0771a24195c3 100644 (file)
@@ -758,8 +758,7 @@ static int linearize(struct x86_emulate_ctxt *ctxt,
                           ctxt->mode, linear);
 }
 
-static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst,
-                            enum x86emul_mode mode)
+static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst)
 {
        ulong linear;
        int rc;
@@ -769,41 +768,71 @@ static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst,
 
        if (ctxt->op_bytes != sizeof(unsigned long))
                addr.ea = dst & ((1UL << (ctxt->op_bytes << 3)) - 1);
-       rc = __linearize(ctxt, addr, &max_size, 1, false, true, mode, &linear);
+       rc = __linearize(ctxt, addr, &max_size, 1, false, true, ctxt->mode, &linear);
        if (rc == X86EMUL_CONTINUE)
                ctxt->_eip = addr.ea;
        return rc;
 }
 
+static inline int emulator_recalc_and_set_mode(struct x86_emulate_ctxt *ctxt)
+{
+       u64 efer;
+       struct desc_struct cs;
+       u16 selector;
+       u32 base3;
+
+       ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
+
+       if (!(ctxt->ops->get_cr(ctxt, 0) & X86_CR0_PE)) {
+               /* Real mode. cpu must not have long mode active */
+               if (efer & EFER_LMA)
+                       return X86EMUL_UNHANDLEABLE;
+               ctxt->mode = X86EMUL_MODE_REAL;
+               return X86EMUL_CONTINUE;
+       }
+
+       if (ctxt->eflags & X86_EFLAGS_VM) {
+               /* Protected/VM86 mode. cpu must not have long mode active */
+               if (efer & EFER_LMA)
+                       return X86EMUL_UNHANDLEABLE;
+               ctxt->mode = X86EMUL_MODE_VM86;
+               return X86EMUL_CONTINUE;
+       }
+
+       if (!ctxt->ops->get_segment(ctxt, &selector, &cs, &base3, VCPU_SREG_CS))
+               return X86EMUL_UNHANDLEABLE;
+
+       if (efer & EFER_LMA) {
+               if (cs.l) {
+                       /* Proper long mode */
+                       ctxt->mode = X86EMUL_MODE_PROT64;
+               } else if (cs.d) {
+                       /* 32 bit compatibility mode*/
+                       ctxt->mode = X86EMUL_MODE_PROT32;
+               } else {
+                       ctxt->mode = X86EMUL_MODE_PROT16;
+               }
+       } else {
+               /* Legacy 32 bit / 16 bit mode */
+               ctxt->mode = cs.d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;
+       }
+
+       return X86EMUL_CONTINUE;
+}
+
 static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
 {
-       return assign_eip(ctxt, dst, ctxt->mode);
+       return assign_eip(ctxt, dst);
 }
 
-static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
-                         const struct desc_struct *cs_desc)
+static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst)
 {
-       enum x86emul_mode mode = ctxt->mode;
-       int rc;
+       int rc = emulator_recalc_and_set_mode(ctxt);
 
-#ifdef CONFIG_X86_64
-       if (ctxt->mode >= X86EMUL_MODE_PROT16) {
-               if (cs_desc->l) {
-                       u64 efer = 0;
+       if (rc != X86EMUL_CONTINUE)
+               return rc;
 
-                       ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
-                       if (efer & EFER_LMA)
-                               mode = X86EMUL_MODE_PROT64;
-               } else
-                       mode = X86EMUL_MODE_PROT32; /* temporary value */
-       }
-#endif
-       if (mode == X86EMUL_MODE_PROT16 || mode == X86EMUL_MODE_PROT32)
-               mode = cs_desc->d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;
-       rc = assign_eip(ctxt, dst, mode);
-       if (rc == X86EMUL_CONTINUE)
-               ctxt->mode = mode;
-       return rc;
+       return assign_eip(ctxt, dst);
 }
 
 static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
@@ -2205,7 +2234,7 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
        if (rc != X86EMUL_CONTINUE)
                return rc;
 
-       rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc);
+       rc = assign_eip_far(ctxt, ctxt->src.val);
        /* Error handling is not implemented. */
        if (rc != X86EMUL_CONTINUE)
                return X86EMUL_UNHANDLEABLE;
@@ -2286,7 +2315,7 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
                                       &new_desc);
        if (rc != X86EMUL_CONTINUE)
                return rc;
-       rc = assign_eip_far(ctxt, eip, &new_desc);
+       rc = assign_eip_far(ctxt, eip);
        /* Error handling is not implemented. */
        if (rc != X86EMUL_CONTINUE)
                return X86EMUL_UNHANDLEABLE;
@@ -3475,7 +3504,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
        if (rc != X86EMUL_CONTINUE)
                return rc;
 
-       rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc);
+       rc = assign_eip_far(ctxt, ctxt->src.val);
        if (rc != X86EMUL_CONTINUE)
                goto fail;