KVM: nSVM: Implement Enlightened MSR-Bitmap feature
authorVitaly Kuznetsov <vkuznets@redhat.com>
Wed, 2 Feb 2022 09:51:00 +0000 (10:51 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 10 Feb 2022 18:50:45 +0000 (13:50 -0500)
Similar to nVMX commit 502d2bf5f2fd ("KVM: nVMX: Implement Enlightened MSR
Bitmap feature"), add support for the feature for nSVM (Hyper-V on KVM).

Notable differences from nVMX implementation:
- As the feature uses SW reserved fields in VMCB control, KVM needs to
make sure it's dealing with a Hyper-V guest (kvm_hv_hypercall_enabled()).

- 'msrpm_base_pa' needs to be always be overwritten in
nested_svm_vmrun_msrpm(), even when the update is skipped. As an
optimization, nested_vmcb02_prepare_control() copies it from VMCB01
so when MSR-Bitmap feature for L2 is disabled nothing needs to be done.

- 'struct vmcb_ctrl_area_cached' needs to be extended with clean
fields/sw reserved data and __nested_copy_vmcb_control_to_cache() needs to
copy it so nested_svm_vmrun_msrpm() can use it later.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Message-Id: <20220202095100.129834-5-vkuznets@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/hyperv.c
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.h

index 683e7ee..653e08c 100644 (file)
@@ -2453,10 +2453,6 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
        if (kvm_x86_ops.nested_ops->get_evmcs_version)
                evmcs_ver = kvm_x86_ops.nested_ops->get_evmcs_version(vcpu);
 
-       /* Skip NESTED_FEATURES if eVMCS is not supported */
-       if (!evmcs_ver)
-               --nent;
-
        if (cpuid->nent < nent)
                return -E2BIG;
 
@@ -2556,8 +2552,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
 
                case HYPERV_CPUID_NESTED_FEATURES:
                        ent->eax = evmcs_ver;
-                       if (evmcs_ver)
-                               ent->eax |= HV_X64_NESTED_MSR_BITMAP;
+                       ent->eax |= HV_X64_NESTED_MSR_BITMAP;
 
                        break;
 
index 74c218b..f284e61 100644 (file)
@@ -28,6 +28,7 @@
 #include "cpuid.h"
 #include "lapic.h"
 #include "svm.h"
+#include "hyperv.h"
 
 #define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK
 
@@ -165,14 +166,30 @@ void recalc_intercepts(struct vcpu_svm *svm)
        vmcb_set_intercept(c, INTERCEPT_VMSAVE);
 }
 
+/*
+ * Merge L0's (KVM) and L1's (Nested VMCB) MSR permission bitmaps. The function
+ * is optimized in that it only merges the parts where KVM MSR permission bitmap
+ * may contain zero bits.
+ */
 static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
 {
+       struct hv_enlightenments *hve =
+               (struct hv_enlightenments *)svm->nested.ctl.reserved_sw;
+       int i;
+
        /*
-        * This function merges the msr permission bitmaps of kvm and the
-        * nested vmcb. It is optimized in that it only merges the parts where
-        * the kvm msr permission bitmap may contain zero bits
+        * MSR bitmap update can be skipped when:
+        * - MSR bitmap for L1 hasn't changed.
+        * - Nested hypervisor (L1) is attempting to launch the same L2 as
+        *   before.
+        * - Nested hypervisor (L1) is using Hyper-V emulation interface and
+        * tells KVM (L0) there were no changes in MSR bitmap for L2.
         */
-       int i;
+       if (!svm->nested.force_msr_bitmap_recalc &&
+           kvm_hv_hypercall_enabled(&svm->vcpu) &&
+           hve->hv_enlightenments_control.msr_bitmap &&
+           (svm->nested.ctl.clean & BIT(VMCB_HV_NESTED_ENLIGHTENMENTS)))
+               goto set_msrpm_base_pa;
 
        if (!(vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)))
                return true;
@@ -195,6 +212,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
 
        svm->nested.force_msr_bitmap_recalc = false;
 
+set_msrpm_base_pa:
        svm->vmcb->control.msrpm_base_pa = __sme_set(__pa(svm->nested.msrpm));
 
        return true;
@@ -300,7 +318,8 @@ static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu)
 }
 
 static
-void __nested_copy_vmcb_control_to_cache(struct vmcb_ctrl_area_cached *to,
+void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu,
+                                        struct vmcb_ctrl_area_cached *to,
                                         struct vmcb_control_area *from)
 {
        unsigned int i;
@@ -333,12 +352,19 @@ void __nested_copy_vmcb_control_to_cache(struct vmcb_ctrl_area_cached *to,
        to->asid           = from->asid;
        to->msrpm_base_pa &= ~0x0fffULL;
        to->iopm_base_pa  &= ~0x0fffULL;
+
+       /* Hyper-V extensions (Enlightened VMCB) */
+       if (kvm_hv_hypercall_enabled(vcpu)) {
+               to->clean = from->clean;
+               memcpy(to->reserved_sw, from->reserved_sw,
+                      sizeof(struct hv_enlightenments));
+       }
 }
 
 void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm,
                                       struct vmcb_control_area *control)
 {
-       __nested_copy_vmcb_control_to_cache(&svm->nested.ctl, control);
+       __nested_copy_vmcb_control_to_cache(&svm->vcpu, &svm->nested.ctl, control);
 }
 
 static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to,
@@ -1305,6 +1331,7 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst,
        dst->virt_ext              = from->virt_ext;
        dst->pause_filter_count   = from->pause_filter_count;
        dst->pause_filter_thresh  = from->pause_filter_thresh;
+       /* 'clean' and 'reserved_sw' are not changed by KVM */
 }
 
 static int svm_get_nested_state(struct kvm_vcpu *vcpu,
@@ -1437,7 +1464,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu,
                goto out_free;
 
        ret = -EINVAL;
-       __nested_copy_vmcb_control_to_cache(&ctl_cached, ctl);
+       __nested_copy_vmcb_control_to_cache(vcpu, &ctl_cached, ctl);
        if (!__nested_vmcb_check_controls(vcpu, &ctl_cached))
                goto out_free;
 
index 365c17b..efcab4d 100644 (file)
@@ -137,6 +137,8 @@ struct vmcb_ctrl_area_cached {
        u32 event_inj_err;
        u64 nested_cr3;
        u64 virt_ext;
+       u32 clean;
+       u8 reserved_sw[32];
 };
 
 struct svm_nested_state {