kvm: nVMX: Shadow "high" parts of shadowed 64-bit VMCS fields
authorJim Mattson <jmattson@google.com>
Fri, 7 Jul 2017 19:51:41 +0000 (12:51 -0700)
committerRadim Krčmář <rkrcmar@redhat.com>
Wed, 12 Jul 2017 16:41:12 +0000 (18:41 +0200)
Inconsistencies result from shadowing only accesses to the full
64-bits of a 64-bit VMCS field, but not shadowing accesses to the high
32-bits of the field. The "high" part of a 64-bit field should be
shadowed whenever the full 64-bit field is shadowed.

Signed-off-by: Jim Mattson <jmattson@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/vmx.c

index e02c7004b64b5c9a00d5ecb94415a75c20d11aca..32db3f5dce7fdd9f178e1d20345cbd97a566fe0d 100644 (file)
@@ -3764,6 +3764,25 @@ static void free_kvm_area(void)
        }
 }
 
+enum vmcs_field_type {
+       VMCS_FIELD_TYPE_U16 = 0,
+       VMCS_FIELD_TYPE_U64 = 1,
+       VMCS_FIELD_TYPE_U32 = 2,
+       VMCS_FIELD_TYPE_NATURAL_WIDTH = 3
+};
+
+static inline int vmcs_field_type(unsigned long field)
+{
+       if (0x1 & field)        /* the *_HIGH fields are all 32 bit */
+               return VMCS_FIELD_TYPE_U32;
+       return (field >> 13) & 0x3 ;
+}
+
+static inline int vmcs_field_readonly(unsigned long field)
+{
+       return (((field >> 10) & 0x3) == 1);
+}
+
 static void init_vmcs_shadow_fields(void)
 {
        int i, j;
@@ -3789,14 +3808,22 @@ static void init_vmcs_shadow_fields(void)
 
        /* shadowed fields guest access without vmexit */
        for (i = 0; i < max_shadow_read_write_fields; i++) {
-               clear_bit(shadow_read_write_fields[i],
-                         vmx_vmwrite_bitmap);
-               clear_bit(shadow_read_write_fields[i],
-                         vmx_vmread_bitmap);
+               unsigned long field = shadow_read_write_fields[i];
+
+               clear_bit(field, vmx_vmwrite_bitmap);
+               clear_bit(field, vmx_vmread_bitmap);
+               if (vmcs_field_type(field) == VMCS_FIELD_TYPE_U64) {
+                       clear_bit(field + 1, vmx_vmwrite_bitmap);
+                       clear_bit(field + 1, vmx_vmread_bitmap);
+               }
+       }
+       for (i = 0; i < max_shadow_read_only_fields; i++) {
+               unsigned long field = shadow_read_only_fields[i];
+
+               clear_bit(field, vmx_vmread_bitmap);
+               if (vmcs_field_type(field) == VMCS_FIELD_TYPE_U64)
+                       clear_bit(field + 1, vmx_vmread_bitmap);
        }
-       for (i = 0; i < max_shadow_read_only_fields; i++)
-               clear_bit(shadow_read_only_fields[i],
-                         vmx_vmread_bitmap);
 }
 
 static __init int alloc_kvm_area(void)
@@ -7219,25 +7246,6 @@ static int handle_vmresume(struct kvm_vcpu *vcpu)
        return nested_vmx_run(vcpu, false);
 }
 
-enum vmcs_field_type {
-       VMCS_FIELD_TYPE_U16 = 0,
-       VMCS_FIELD_TYPE_U64 = 1,
-       VMCS_FIELD_TYPE_U32 = 2,
-       VMCS_FIELD_TYPE_NATURAL_WIDTH = 3
-};
-
-static inline int vmcs_field_type(unsigned long field)
-{
-       if (0x1 & field)        /* the *_HIGH fields are all 32 bit */
-               return VMCS_FIELD_TYPE_U32;
-       return (field >> 13) & 0x3 ;
-}
-
-static inline int vmcs_field_readonly(unsigned long field)
-{
-       return (((field >> 10) & 0x3) == 1);
-}
-
 /*
  * Read a vmcs12 field. Since these can have varying lengths and we return
  * one type, we chose the biggest type (u64) and zero-extend the return value