Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed7ae7cc authored by Maxim Levitsky's avatar Maxim Levitsky Committed by Greg Kroah-Hartman
Browse files

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: 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>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4e3cd22f
Loading
Loading
Loading
Loading
+57 −28
Original line number Diff line number Diff line
@@ -759,8 +759,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;
@@ -770,41 +769,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 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;

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;
	ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);

#ifdef CONFIG_X86_64
	if (ctxt->mode >= X86EMUL_MODE_PROT16) {
		if (cs_desc->l) {
			u64 efer = 0;
	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;
	}

			ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
	if (ctxt->eflags & X86_EFLAGS_VM) {
		/* Protected/VM86 mode. cpu must not have long mode active */
		if (efer & EFER_LMA)
				mode = X86EMUL_MODE_PROT64;
		} else
			mode = X86EMUL_MODE_PROT32; /* temporary value */
			return X86EMUL_UNHANDLEABLE;
		ctxt->mode = X86EMUL_MODE_VM86;
		return X86EMUL_CONTINUE;
	}
#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;

	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);
}

static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst)
{
	int rc = emulator_recalc_and_set_mode(ctxt);

	if (rc != X86EMUL_CONTINUE)
		return rc;

	return assign_eip(ctxt, dst);
}

static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
@@ -2192,7 +2221,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;
@@ -2273,7 +2302,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;
@@ -3492,7 +3521,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;