4fc427d906
(generated with debian/scripts/import-upstream-tag) Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
159 lines
4.8 KiB
Diff
159 lines
4.8 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Maxim Levitsky <mlevitsk@redhat.com>
|
|
Date: Wed, 3 Aug 2022 18:50:01 +0300
|
|
Subject: [PATCH] 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 it.
|
|
|
|
No functional change is intended.
|
|
|
|
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
|
|
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
|
|
---
|
|
arch/x86/kvm/emulate.c | 85 ++++++++++++++++++++++++++++--------------
|
|
1 file changed, 57 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
|
|
index 13181819d52c..9411046e9ee0 100644
|
|
--- a/arch/x86/kvm/emulate.c
|
|
+++ b/arch/x86/kvm/emulate.c
|
|
@@ -793,8 +793,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;
|
|
@@ -804,41 +803,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)
|
|
@@ -2172,7 +2201,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;
|
|
@@ -2250,7 +2279,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;
|
|
@@ -3470,7 +3499,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;
|
|
|