Commit 66c03a92 authored by Vitaly Kuznetsov's avatar Vitaly Kuznetsov Committed by Paolo Bonzini

KVM: nSVM: Implement Enlightened MSR-Bitmap feature

Similar to nVMX commit 502d2bf5 ("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: default avatarVitaly Kuznetsov <vkuznets@redhat.com>
Message-Id: <20220202095100.129834-5-vkuznets@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 9e083ec7
...@@ -2453,10 +2453,6 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid, ...@@ -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) if (kvm_x86_ops.nested_ops->get_evmcs_version)
evmcs_ver = kvm_x86_ops.nested_ops->get_evmcs_version(vcpu); 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) if (cpuid->nent < nent)
return -E2BIG; return -E2BIG;
...@@ -2556,8 +2552,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid, ...@@ -2556,8 +2552,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
case HYPERV_CPUID_NESTED_FEATURES: case HYPERV_CPUID_NESTED_FEATURES:
ent->eax = evmcs_ver; ent->eax = evmcs_ver;
if (evmcs_ver) ent->eax |= HV_X64_NESTED_MSR_BITMAP;
ent->eax |= HV_X64_NESTED_MSR_BITMAP;
break; break;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "cpuid.h" #include "cpuid.h"
#include "lapic.h" #include "lapic.h"
#include "svm.h" #include "svm.h"
#include "hyperv.h"
#define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK #define CC KVM_NESTED_VMENTER_CONSISTENCY_CHECK
...@@ -165,14 +166,30 @@ void recalc_intercepts(struct vcpu_svm *svm) ...@@ -165,14 +166,30 @@ void recalc_intercepts(struct vcpu_svm *svm)
vmcb_set_intercept(c, INTERCEPT_VMSAVE); 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) 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 * MSR bitmap update can be skipped when:
* nested vmcb. It is optimized in that it only merges the parts where * - MSR bitmap for L1 hasn't changed.
* the kvm msr permission bitmap may contain zero bits * - 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))) if (!(vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)))
return true; return true;
...@@ -195,6 +212,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) ...@@ -195,6 +212,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
svm->nested.force_msr_bitmap_recalc = false; svm->nested.force_msr_bitmap_recalc = false;
set_msrpm_base_pa:
svm->vmcb->control.msrpm_base_pa = __sme_set(__pa(svm->nested.msrpm)); svm->vmcb->control.msrpm_base_pa = __sme_set(__pa(svm->nested.msrpm));
return true; return true;
...@@ -300,7 +318,8 @@ static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu) ...@@ -300,7 +318,8 @@ static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu)
} }
static 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) struct vmcb_control_area *from)
{ {
unsigned int i; unsigned int i;
...@@ -333,12 +352,19 @@ void __nested_copy_vmcb_control_to_cache(struct vmcb_ctrl_area_cached *to, ...@@ -333,12 +352,19 @@ void __nested_copy_vmcb_control_to_cache(struct vmcb_ctrl_area_cached *to,
to->asid = from->asid; to->asid = from->asid;
to->msrpm_base_pa &= ~0x0fffULL; to->msrpm_base_pa &= ~0x0fffULL;
to->iopm_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, void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm,
struct vmcb_control_area *control) 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, 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, ...@@ -1305,6 +1331,7 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst,
dst->virt_ext = from->virt_ext; dst->virt_ext = from->virt_ext;
dst->pause_filter_count = from->pause_filter_count; dst->pause_filter_count = from->pause_filter_count;
dst->pause_filter_thresh = from->pause_filter_thresh; 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, static int svm_get_nested_state(struct kvm_vcpu *vcpu,
...@@ -1437,7 +1464,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, ...@@ -1437,7 +1464,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu,
goto out_free; goto out_free;
ret = -EINVAL; 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)) if (!__nested_vmcb_check_controls(vcpu, &ctl_cached))
goto out_free; goto out_free;
......
...@@ -137,6 +137,8 @@ struct vmcb_ctrl_area_cached { ...@@ -137,6 +137,8 @@ struct vmcb_ctrl_area_cached {
u32 event_inj_err; u32 event_inj_err;
u64 nested_cr3; u64 nested_cr3;
u64 virt_ext; u64 virt_ext;
u32 clean;
u8 reserved_sw[32];
}; };
struct svm_nested_state { struct svm_nested_state {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment