diff mbox series

[3/4] target/i386: Added consistency checks for EFER

Message ID 20210705081802.18960-4-laramglazier@gmail.com
State New
Headers show
Series target/i386: Continuing fixing kvm-unit-tests for svm | expand

Commit Message

Lara Lazier July 5, 2021, 8:18 a.m. UTC
EFER.SVME has to be set, and EFER[63:16], EFER[9], EFER[7:5]
are reserved and must be zero.
In Addition the combinations
 * EFER.LMA or EFER.LME is non-zero and the processor does not support LM
 * non-zero EFER.LME and CR0.PG and zero CR4.PAE
 * non-zero EFER.LME and CR0.PG and zero CR0.PE
 * non-zero EFER.LME, CR0.PG, CR4.PAE, CS.L and CS.D
are all invalid.
(AMD64 Architecture Programmer's Manual, V2, 15.5)

Signed-off-by: Lara Lazier <laramglazier@gmail.com>
---
 target/i386/cpu.h                   |  2 ++
 target/i386/tcg/sysemu/svm_helper.c | 39 +++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)

Comments

Paolo Bonzini July 6, 2021, 4:48 p.m. UTC | #1
> EFER.SVME has to be set, and EFER[63:16], EFER[9], EFER[7:5]
> are reserved and must be zero.

My version of the manual says "any MBZ [must-be-zero] bit of EFER is 
set", so that would be 7:1 (not 7:5), 9 and 63:16.  In QEMU bits 13 and 
15 are also unimplemented and thus must-be-zero.

On 05/07/21 10:18, Lara Lazier wrote:
> +#define MSR_EFER_RESERVED 0xffffffffffff02e0
> +

This has the same issue with 32-bit compilation, except in this case the 
check *is* needed on 32-bit builds just without bits 63:32 set.

The obvious way here would be to add a #ifdef, but that's less 
maintainable than the slightly ugly:

#define MSR_EFER_RESERVED               ((target_ulong)(int)0xffff02e0u)

(where I was too lazy to compute the right mask for the bits I listed 
above...).

Paolo
diff mbox series

Patch

diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index f7fa5870b1..f5280b2951 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -464,6 +464,8 @@  typedef enum X86Seg {
 #define MSR_EFER_SVME  (1 << 12)
 #define MSR_EFER_FFXSR (1 << 14)
 
+#define MSR_EFER_RESERVED 0xffffffffffff02e0
+
 #define MSR_STAR                        0xc0000081
 #define MSR_LSTAR                       0xc0000082
 #define MSR_CSTAR                       0xc0000083
diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c
index 276c240f70..d652d6f9da 100644
--- a/target/i386/tcg/sysemu/svm_helper.c
+++ b/target/i386/tcg/sysemu/svm_helper.c
@@ -65,6 +65,41 @@  static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr,
                            sc->base, sc->limit, sc->flags);
 }
 
+static inline bool is_efer_invalid_state (CPUX86State *env)
+{
+    if (!(env->efer & MSR_EFER_SVME)) {
+        return true;
+    }
+
+    if (env->efer & MSR_EFER_RESERVED) {
+        return true;
+    }
+
+    if ((env->efer & (MSR_EFER_LMA | MSR_EFER_LME)) &&
+            !(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) {
+        return true;
+    }
+
+    if ((env->efer & MSR_EFER_LME) && (env->cr[0] & CR0_PG_MASK)
+                                && !(env->cr[4] & CR4_PAE_MASK)) {
+        return true;
+    }
+
+    if ((env->efer & MSR_EFER_LME) && (env->cr[0] & CR0_PG_MASK)
+                                && !(env->cr[0] & CR0_PE_MASK)) {
+        return true;
+    }
+
+    if ((env->efer & MSR_EFER_LME) && (env->cr[0] & CR0_PG_MASK)
+                                && (env->cr[4] & CR4_PAE_MASK)
+                                && (env->segs[R_CS].flags & DESC_L_MASK)
+                                && (env->segs[R_CS].flags & DESC_B_MASK)) {
+        return true;
+    }
+
+    return false;
+}
+
 void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
 {
     CPUState *cs = env_cpu(env);
@@ -277,6 +312,10 @@  void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
         cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC());
     }
 
+    if (is_efer_invalid_state(env)) {
+        cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC());
+    }
+
     switch (x86_ldub_phys(cs,
                       env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
     case TLB_CONTROL_DO_NOTHING: