Commit d087e0f7 authored by Maxim Levitsky's avatar Maxim Levitsky Committed by Paolo Bonzini

KVM: x86: emulator: introduce emulator_recalc_and_set_mode

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: default avatarMaxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20221025124741.228045-12-mlevitsk@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 5015bb89
...@@ -791,8 +791,7 @@ static int linearize(struct x86_emulate_ctxt *ctxt, ...@@ -791,8 +791,7 @@ static int linearize(struct x86_emulate_ctxt *ctxt,
ctxt->mode, linear); ctxt->mode, linear);
} }
static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst, static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst)
enum x86emul_mode mode)
{ {
ulong linear; ulong linear;
int rc; int rc;
...@@ -802,41 +801,71 @@ static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst, ...@@ -802,41 +801,71 @@ static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst,
if (ctxt->op_bytes != sizeof(unsigned long)) if (ctxt->op_bytes != sizeof(unsigned long))
addr.ea = dst & ((1UL << (ctxt->op_bytes << 3)) - 1); 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) if (rc == X86EMUL_CONTINUE)
ctxt->_eip = addr.ea; ctxt->_eip = addr.ea;
return rc; 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) 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, static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst)
const struct desc_struct *cs_desc)
{ {
enum x86emul_mode mode = ctxt->mode; int rc = emulator_recalc_and_set_mode(ctxt);
int rc;
#ifdef CONFIG_X86_64 if (rc != X86EMUL_CONTINUE)
if (ctxt->mode >= X86EMUL_MODE_PROT16) { return rc;
if (cs_desc->l) {
u64 efer = 0;
ctxt->ops->get_msr(ctxt, MSR_EFER, &efer); return assign_eip(ctxt, dst);
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;
} }
static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel) static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
...@@ -2172,7 +2201,7 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) ...@@ -2172,7 +2201,7 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; 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. */ /* Error handling is not implemented. */
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return X86EMUL_UNHANDLEABLE; return X86EMUL_UNHANDLEABLE;
...@@ -2250,7 +2279,7 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt) ...@@ -2250,7 +2279,7 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
&new_desc); &new_desc);
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
rc = assign_eip_far(ctxt, eip, &new_desc); rc = assign_eip_far(ctxt, eip);
/* Error handling is not implemented. */ /* Error handling is not implemented. */
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return X86EMUL_UNHANDLEABLE; return X86EMUL_UNHANDLEABLE;
...@@ -3470,7 +3499,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt) ...@@ -3470,7 +3499,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
return rc; return rc;
rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc); rc = assign_eip_far(ctxt, ctxt->src.val);
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE)
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment