KVM: x86: emulator: introduce emulator_recalc_and_set_mode
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>
This commit is contained in:
parent
c8a2fd7a71
commit
ce9261accc
@ -796,8 +796,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;
|
||||||
@ -807,41 +806,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 assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
|
static inline int emulator_recalc_and_set_mode(struct x86_emulate_ctxt *ctxt)
|
||||||
{
|
{
|
||||||
return assign_eip(ctxt, dst, ctxt->mode);
|
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 int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
|
static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
|
||||||
const struct desc_struct *cs_desc)
|
|
||||||
{
|
{
|
||||||
enum x86emul_mode mode = ctxt->mode;
|
return assign_eip(ctxt, dst);
|
||||||
int rc;
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst)
|
||||||
if (ctxt->mode >= X86EMUL_MODE_PROT16) {
|
{
|
||||||
if (cs_desc->l) {
|
int rc = emulator_recalc_and_set_mode(ctxt);
|
||||||
u64 efer = 0;
|
|
||||||
|
|
||||||
ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
|
if (rc != X86EMUL_CONTINUE)
|
||||||
if (efer & EFER_LMA)
|
return rc;
|
||||||
mode = X86EMUL_MODE_PROT64;
|
|
||||||
} else
|
return assign_eip(ctxt, dst);
|
||||||
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)
|
||||||
@ -2256,7 +2285,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;
|
||||||
@ -2337,7 +2366,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;
|
||||||
@ -3554,7 +3583,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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user