[PATCH 19/22] KVM: nVMX: Add utils to copy shadow vmcs02 to cached shadow vmcs12 and vice-versa

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is done as a preparation for VMCS shadowing virtualization.

Signed-off-by: Liran Alon <liran.alon@xxxxxxxxxx>
Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx>
---
 arch/x86/kvm/vmx.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 128 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 72bd4b6a90fc..e3f3bb5102f0 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -4513,7 +4513,8 @@ enum vmcs_field_width {
 	VMCS_FIELD_WIDTH_U16 = 0,
 	VMCS_FIELD_WIDTH_U64 = 1,
 	VMCS_FIELD_WIDTH_U32 = 2,
-	VMCS_FIELD_WIDTH_NATURAL_WIDTH = 3
+	VMCS_FIELD_WIDTH_NATURAL_WIDTH = 3,
+	VMCS_FIELD_WIDTHS,
 };
 
 static inline int vmcs_field_width(unsigned long field)
@@ -4527,7 +4528,8 @@ enum vmcs_field_type {
 	VMCS_FIELD_TYPE_CONTROL = 0,
 	VMCS_FIELD_TYPE_READ_ONLY_DATA = 1,
 	VMCS_FIELD_TYPE_GUEST = 2,
-	VMCS_FIELD_TYPE_HOST = 3
+	VMCS_FIELD_TYPE_HOST = 3,
+	VMCS_FIELD_TYPES,
 };
 
 static inline int vmcs_field_type(unsigned long field)
@@ -8324,6 +8326,70 @@ static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx)
 	preempt_enable();
 }
 
+/*
+ * The IA32_VMX_VMCS_ENUM MSR reports the highest index value (bits
+ * 9:1) used in the encoding of any vmcs field supported by the processor.
+ * For 64 bit-values, bit 0 of the encoding can be set to access only
+ * the high 32-bits of the field. Hence, the maxiumum number of fields
+ * with the same field width and type is the value reported by this
+ * MSR plus two.
+ */
+static unsigned nested_vmcs_fields_per_group(struct vcpu_vmx *vmx)
+{
+	return vmx->nested.msrs.vmcs_enum + 2;
+}
+
+/*
+ * Copy a group of VMCS fields (fields having the same width and type)
+ * from the hardware VMCS shadow to the cached guest VMCS shadow. Only
+ * fields with vmwrite permission are copied, since they are the only
+ * fields that can change while running L2.
+ * This function assumes nested_vmcs_field_per_group() <=
+ * BITS_PER_LONG, a constraint that is checked in prepare_vmcs02().
+ */
+static void copy_shadow_vmcs02_to_shadow_vmcs12_group(struct vcpu_vmx *vmx,
+						      unsigned long base)
+{
+	unsigned long bit;
+	unsigned long offset = base / BITS_PER_LONG;
+
+	for_each_clear_bit(bit, vmx->nested.vmwrite_bitmap + offset,
+			   nested_vmcs_fields_per_group(vmx)) {
+		unsigned long field = base + bit;
+		u64 field_value = __vmcs_readl(field);
+		vmcs12_write_any(get_shadow_vmcs12(&vmx->vcpu),
+				 field, field_value);
+	}
+}
+
+/*
+ * Copy any potentially modified VMCS fields from the hardware VMCS
+ * shadow to the cached guest VMCS shadow.
+ */
+static void copy_shadow_vmcs02_to_shadow_vmcs12(struct vcpu_vmx *vmx)
+{
+	enum vmcs_field_width width;
+	enum vmcs_field_type type;
+
+	/* Should be called when vmcs02 is loaded */
+	WARN_ON(vmx->loaded_vmcs == &vmx->vmcs01);
+
+	preempt_disable();
+
+	vmcs_load(vmx->loaded_vmcs->shadow_vmcs);
+
+	for (width = 0; width < VMCS_FIELD_WIDTHS; width++) {
+		for (type = 0; type < VMCS_FIELD_TYPES; type++) {
+			unsigned long base = encode_vmcs_field(width, type, 0, false);
+			copy_shadow_vmcs02_to_shadow_vmcs12_group(vmx, base);
+		}
+	}
+
+	vmcs_load(vmx->loaded_vmcs->vmcs);
+
+	preempt_enable();
+}
+
 static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx)
 {
 	const u16 *fields[] = {
@@ -8354,6 +8420,58 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx)
 }
 
 /*
+ * Copy a group of VMCS fields (fields having the same width and type)
+ * from the cached guest VMCS shadow to the hardware VMCS shadow. Only
+ * fields with vmread or vmwrite permission are copied.
+ * This function assumes nested_vmcs_fields_per_group() <=
+ * BITS_PER_LONG, a constraint that is checked in prepare_vmcs02().
+ */
+static void copy_shadow_vmcs12_to_shadow_vmcs02_group(struct vcpu_vmx *vmx,
+						      unsigned long base)
+{
+	unsigned long bit;
+	unsigned long offset = base / BITS_PER_LONG;
+	unsigned long merged = (vmx->nested.vmread_bitmap[offset] &
+				vmx->nested.vmwrite_bitmap[offset]);
+
+	for_each_clear_bit(bit, &merged, nested_vmcs_fields_per_group(vmx)) {
+		unsigned long field = base + bit;
+		u64 field_value;
+		if (!vmcs12_read_any(get_shadow_vmcs12(&vmx->vcpu),
+				     field, &field_value))
+			__vmcs_writel(field, field_value);
+	}
+}
+
+/*
+ * Copy any readable or writable VMCS fields from the cached guest VMCS
+ * shadow to the hardware VMCS shadow.
+ */
+static void copy_shadow_vmcs12_to_shadow_vmcs02(struct vcpu_vmx *vmx)
+{
+	enum vmcs_field_width width;
+	enum vmcs_field_type type;
+
+	/* Should be called when vmcs02 is loaded */
+	WARN_ON(vmx->loaded_vmcs == &vmx->vmcs01);
+
+	preempt_disable();
+
+	vmcs_load(vmx->loaded_vmcs->shadow_vmcs);
+
+	for (width = 0; width < VMCS_FIELD_WIDTHS; width++) {
+		for (type = 0; type < VMCS_FIELD_TYPES; type++) {
+			unsigned long base = encode_vmcs_field(width, type, 0, false);
+			copy_shadow_vmcs12_to_shadow_vmcs02_group(vmx, base);
+		}
+	}
+
+	vmcs_load(vmx->loaded_vmcs->vmcs);
+
+	preempt_enable();
+}
+
+/*
  * VMX instructions which assume a current vmcs12 (i.e., that VMPTRLD was
  * used before) all generate the same failure when it is missing.
  */
@@ -11639,9 +11757,16 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
 		 * Otherwise, emulate VMCS shadowing by disabling VMCS
 		 * shadowing at vmcs02 and emulate vmread/vmwrite to
 		 * read/write from/to shadow vmcs12.
+		 *
+		 * Note: Due to limitations of
+		 * copy_shadow_vmcs12_to_shadow_vmcs02_group(), fall
+		 * back to VMCS shadowing emulation if
+		 * nested_vmcs_fields_per_group() > BITS_PER_LONG.
 		 */
 		if ((exec_control & SECONDARY_EXEC_SHADOW_VMCS) &&
-		    enable_shadow_vmcs && !alloc_vmcs_shadowing_pages(vcpu)) {
+		    enable_shadow_vmcs &&
+		    nested_vmcs_fields_per_group(vmx) <= BITS_PER_LONG &&
+		    !alloc_vmcs_shadowing_pages(vcpu)) {
 			vmcs_write64(VMCS_LINK_POINTER,
 				     vmcs12->vmcs_link_pointer == -1ull ?
 				     -1ull : __pa(vmx->loaded_vmcs->shadow_vmcs));
-- 
1.9.1




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux