KVM: nVMX: Allow VMREAD when Enlightened VMCS is in use
Hyper-V TLFS explicitly forbids VMREAD and VMWRITE instructions when Enlightened VMCS interface is in use: "Any VMREAD or VMWRITE instructions while an enlightened VMCS is active is unsupported and can result in unexpected behavior."" Windows 11 + WSL2 seems to ignore this, attempts to VMREAD VMCS field 0x4404 ("VM-exit interruption information") are observed. Failing these attempts with nested_vmx_failInvalid() makes such guests unbootable. Microsoft confirms this is a Hyper-V bug and claims that it'll get fixed eventually but for the time being we need a workaround. (Temporary) allow VMREAD to get data from the currently loaded Enlightened VMCS. Note: VMWRITE instructions remain forbidden, it is not clear how to handle them properly and hopefully won't ever be needed. Reviewed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Message-Id: <20220112170134.1904308-6-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Родитель
892a42c10d
Коммит
6cbbaab60f
|
@ -98,6 +98,18 @@ static __always_inline int evmcs_field_offset(unsigned long field,
|
|||
return evmcs_field->offset;
|
||||
}
|
||||
|
||||
static inline u64 evmcs_read_any(struct hv_enlightened_vmcs *evmcs,
|
||||
unsigned long field, u16 offset)
|
||||
{
|
||||
/*
|
||||
* vmcs12_read_any() doesn't care whether the supplied structure
|
||||
* is 'struct vmcs12' or 'struct hv_enlightened_vmcs' as it takes
|
||||
* the exact offset of the required field, use it for convenience
|
||||
* here.
|
||||
*/
|
||||
return vmcs12_read_any((void *)evmcs, field, offset);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_HYPERV)
|
||||
|
||||
static __always_inline int get_evmcs_offset(unsigned long field,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <asm/mmu_context.h>
|
||||
|
||||
#include "cpuid.h"
|
||||
#include "evmcs.h"
|
||||
#include "hyperv.h"
|
||||
#include "mmu.h"
|
||||
#include "nested.h"
|
||||
|
@ -5101,6 +5102,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
|
|||
if (!nested_vmx_check_permission(vcpu))
|
||||
return 1;
|
||||
|
||||
/* Decode instruction info and find the field to read */
|
||||
field = kvm_register_read(vcpu, (((instr_info) >> 28) & 0xf));
|
||||
|
||||
if (!evmptr_is_valid(vmx->nested.hv_evmcs_vmptr)) {
|
||||
/*
|
||||
* In VMX non-root operation, when the VMCS-link pointer is INVALID_GPA,
|
||||
* any VMREAD sets the ALU flags for VMfailInvalid.
|
||||
|
@ -5110,9 +5115,6 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
|
|||
get_vmcs12(vcpu)->vmcs_link_pointer == INVALID_GPA))
|
||||
return nested_vmx_failInvalid(vcpu);
|
||||
|
||||
/* Decode instruction info and find the field to read */
|
||||
field = kvm_register_read(vcpu, (((instr_info) >> 28) & 0xf));
|
||||
|
||||
offset = get_vmcs12_field_offset(field);
|
||||
if (offset < 0)
|
||||
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
|
||||
|
@ -5122,6 +5124,27 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* Read the field, zero-extended to a u64 value */
|
||||
value = vmcs12_read_any(vmcs12, field, offset);
|
||||
} else {
|
||||
/*
|
||||
* Hyper-V TLFS (as of 6.0b) explicitly states, that while an
|
||||
* enlightened VMCS is active VMREAD/VMWRITE instructions are
|
||||
* unsupported. Unfortunately, certain versions of Windows 11
|
||||
* don't comply with this requirement which is not enforced in
|
||||
* genuine Hyper-V. Allow VMREAD from an enlightened VMCS as a
|
||||
* workaround, as misbehaving guests will panic on VM-Fail.
|
||||
* Note, enlightened VMCS is incompatible with shadow VMCS so
|
||||
* all VMREADs from L2 should go to L1.
|
||||
*/
|
||||
if (WARN_ON_ONCE(is_guest_mode(vcpu)))
|
||||
return nested_vmx_failInvalid(vcpu);
|
||||
|
||||
offset = evmcs_field_offset(field, NULL);
|
||||
if (offset < 0)
|
||||
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
|
||||
|
||||
/* Read the field, zero-extended to a u64 value */
|
||||
value = evmcs_read_any(vmx->nested.hv_evmcs, field, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now copy part of this value to register or memory, as requested.
|
||||
|
|
Загрузка…
Ссылка в новой задаче