diff mbox

[20/22] KVM: nVMX: Add util to create vmcs02 vmread/vmwrite bitmaps

Message ID 1529710522-28315-21-git-send-email-liran.alon@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Liran Alon June 22, 2018, 11:35 p.m. UTC
These bitmaps are based on vmcs12 vmread/vmwrite bitmaps and which
VMCS fields are supported by vmcs12 & physical CPU.

This is done as a preparation for VMCS shadowing virtualization.

Signed-off-by: Liran Alon <liran.alon@oracle.com>
Signed-off-by: Jim Mattson <jmattson@google.com>
---
 arch/x86/kvm/vmx.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

Comments

Paolo Bonzini July 18, 2018, 9:02 a.m. UTC | #1
On 23/06/2018 01:35, Liran Alon wrote:
> +	for_each_clear_bit(bit, vmcs12_bitmap + offset,
> +			   nested_vmcs_fields_per_group(vmx)) {

You cannot be sure that nested_vmcs_fields_per_group(vmx) <= 64; the
index spans bits 1 to 9, so it can be at most 1024.  In fact, even hough
it is currently the case that it is less than 64, this code already
breaks on 32-bit machines since guest interruptibility state is
00004824H for example.

Here I think it's much better if we already do the optimization you
mention in the cover letter, of computing vmcs_field_to_offset(field) >=
0 && cpu_has_vmcs_field(field) at startup.  Then this loop becomes just
a word-by-word AND of vmcs12_bitmap with the bitmap you computed at
startup; really just a single AND on 64-bit machines and two for 32-bits.

Thanks,

Paolo

> +		unsigned long field = base + bit;
> +
> +		if (vmcs_field_to_offset(field) >= 0 &&
> +		    cpu_has_vmcs_field(field))
> +			val &= ~(1ul << bit);
> +	}
> +
> +	vmcs02_bitmap[offset] = val;
Jim Mattson July 18, 2018, 4:44 p.m. UTC | #2
Actually, I am sure that nested_vmcs_fields_per_group(vmx) <= 64,
because I fall back to emulation otherwise. But I'd be happy if
someone wanted to make this better.

On Wed, Jul 18, 2018 at 2:02 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 23/06/2018 01:35, Liran Alon wrote:
>> +     for_each_clear_bit(bit, vmcs12_bitmap + offset,
>> +                        nested_vmcs_fields_per_group(vmx)) {
>
> You cannot be sure that nested_vmcs_fields_per_group(vmx) <= 64; the
> index spans bits 1 to 9, so it can be at most 1024.  In fact, even hough
> it is currently the case that it is less than 64, this code already
> breaks on 32-bit machines since guest interruptibility state is
> 00004824H for example.
>
> Here I think it's much better if we already do the optimization you
> mention in the cover letter, of computing vmcs_field_to_offset(field) >=
> 0 && cpu_has_vmcs_field(field) at startup.  Then this loop becomes just
> a word-by-word AND of vmcs12_bitmap with the bitmap you computed at
> startup; really just a single AND on 64-bit machines and two for 32-bits.
>
> Thanks,
>
> Paolo
>
>> +             unsigned long field = base + bit;
>> +
>> +             if (vmcs_field_to_offset(field) >= 0 &&
>> +                 cpu_has_vmcs_field(field))
>> +                     val &= ~(1ul << bit);
>> +     }
>> +
>> +     vmcs02_bitmap[offset] = val;
>
diff mbox

Patch

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index e3f3bb5102f0..94922adf6f47 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -11162,6 +11162,103 @@  static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu,
 	return true;
 }
 
+/*
+ * For all valid VMCS fields in a group (having the same width and
+ * type), clear the permission bit in the vmcs02 bitmap if it is clear
+ * in the vmcs12 bitmap and the field is supported by kvm and the
+ * physical CPU. All other permission bits are set.
+ */
+static void nested_vmx_setup_shadow_bitmap_group(struct kvm_vcpu *vcpu,
+						 unsigned long *vmcs02_bitmap,
+						 unsigned long *vmcs12_bitmap,
+						 unsigned long base)
+{
+	unsigned long offset = base / BITS_PER_LONG;
+	unsigned long val = ~0l;
+	unsigned long bit;
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+	for_each_clear_bit(bit, vmcs12_bitmap + offset,
+			   nested_vmcs_fields_per_group(vmx)) {
+		unsigned long field = base + bit;
+
+		if (vmcs_field_to_offset(field) >= 0 &&
+		    cpu_has_vmcs_field(field))
+			val &= ~(1ul << bit);
+	}
+
+	vmcs02_bitmap[offset] = val;
+}
+
+/*
+ * Set up a vmcs02 vmread/vmwrite bitmap based on a vmcs12
+ * vmread/vmwrite bitmap. Only fields supported by kvm and the
+ * physical CPU may be shadowed (i.e. have a clear permission bit).
+ */
+static bool nested_vmx_setup_shadow_bitmap(struct kvm_vcpu *vcpu,
+					   unsigned long *vmcs02_bitmap,
+					   gpa_t vmcs12_bitmap_gpa,
+					   bool is_write_bitmap)
+{
+	struct page *page;
+	unsigned long *vmcs12_bitmap;
+	enum vmcs_field_width width;
+	enum vmcs_field_type type;
+	bool intercept_read_only_fields;
+
+	page = kvm_vcpu_gpa_to_page(vcpu, vmcs12_bitmap_gpa);
+	if (is_error_page(page))
+		return false;
+
+	/*
+	 * If L1 cannot VMWRITE into read-only VMCS fields,
+	 * then L0 needs to intercept VMWRITEs to these fields
+	 * in order to simulate VMWRITE failure for L2.
+	 */
+	intercept_read_only_fields =
+		is_write_bitmap &&
+		!nested_cpu_has_vmwrite_any_field(vcpu);
+
+	vmcs12_bitmap = (unsigned long *)kmap(page);
+	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);
+			if (intercept_read_only_fields &&
+			    (type == VMCS_FIELD_TYPE_READ_ONLY_DATA))
+				vmcs02_bitmap[base / BITS_PER_LONG] = ~0l;
+			else
+				nested_vmx_setup_shadow_bitmap_group(vcpu,
+							     vmcs02_bitmap,
+							     vmcs12_bitmap,
+							     base);
+		}
+	}
+	kunmap(page);
+
+	kvm_release_page_clean(page);
+
+	return true;
+}
+
+/*
+ * Set up the vmcs02 vmread and vmwrite permission bitmaps based on
+ * the corresponding vmcs12 permission bitmaps.
+ */
+static inline bool nested_vmx_setup_shadow_bitmaps(struct kvm_vcpu *vcpu,
+						   struct vmcs12 *vmcs12)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+	return nested_vmx_setup_shadow_bitmap(vcpu,
+					      vmx->nested.vmread_bitmap,
+					      vmcs12->vmread_bitmap,
+					      false) &&
+		nested_vmx_setup_shadow_bitmap(vcpu,
+					       vmx->nested.vmwrite_bitmap,
+					       vmcs12->vmwrite_bitmap,
+					       true);
+}
+
 static void nested_cache_shadow_vmcs12(struct kvm_vcpu *vcpu,
 				       struct vmcs12 *vmcs12)
 {