Skip to content

Commit 6cbbaab

Browse files
vittyvkbonzini
authored andcommitted
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 <[email protected]> Signed-off-by: Vitaly Kuznetsov <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 892a42c commit 6cbbaab

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

arch/x86/kvm/vmx/evmcs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ static __always_inline int evmcs_field_offset(unsigned long field,
9898
return evmcs_field->offset;
9999
}
100100

101+
static inline u64 evmcs_read_any(struct hv_enlightened_vmcs *evmcs,
102+
unsigned long field, u16 offset)
103+
{
104+
/*
105+
* vmcs12_read_any() doesn't care whether the supplied structure
106+
* is 'struct vmcs12' or 'struct hv_enlightened_vmcs' as it takes
107+
* the exact offset of the required field, use it for convenience
108+
* here.
109+
*/
110+
return vmcs12_read_any((void *)evmcs, field, offset);
111+
}
112+
101113
#if IS_ENABLED(CONFIG_HYPERV)
102114

103115
static __always_inline int get_evmcs_offset(unsigned long field,

arch/x86/kvm/vmx/nested.c

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <asm/mmu_context.h>
88

99
#include "cpuid.h"
10+
#include "evmcs.h"
1011
#include "hyperv.h"
1112
#include "mmu.h"
1213
#include "nested.h"
@@ -5101,27 +5102,49 @@ static int handle_vmread(struct kvm_vcpu *vcpu)
51015102
if (!nested_vmx_check_permission(vcpu))
51025103
return 1;
51035104

5104-
/*
5105-
* In VMX non-root operation, when the VMCS-link pointer is INVALID_GPA,
5106-
* any VMREAD sets the ALU flags for VMfailInvalid.
5107-
*/
5108-
if (vmx->nested.current_vmptr == INVALID_GPA ||
5109-
(is_guest_mode(vcpu) &&
5110-
get_vmcs12(vcpu)->vmcs_link_pointer == INVALID_GPA))
5111-
return nested_vmx_failInvalid(vcpu);
5112-
51135105
/* Decode instruction info and find the field to read */
51145106
field = kvm_register_read(vcpu, (((instr_info) >> 28) & 0xf));
51155107

5116-
offset = get_vmcs12_field_offset(field);
5117-
if (offset < 0)
5118-
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
5108+
if (!evmptr_is_valid(vmx->nested.hv_evmcs_vmptr)) {
5109+
/*
5110+
* In VMX non-root operation, when the VMCS-link pointer is INVALID_GPA,
5111+
* any VMREAD sets the ALU flags for VMfailInvalid.
5112+
*/
5113+
if (vmx->nested.current_vmptr == INVALID_GPA ||
5114+
(is_guest_mode(vcpu) &&
5115+
get_vmcs12(vcpu)->vmcs_link_pointer == INVALID_GPA))
5116+
return nested_vmx_failInvalid(vcpu);
51195117

5120-
if (!is_guest_mode(vcpu) && is_vmcs12_ext_field(field))
5121-
copy_vmcs02_to_vmcs12_rare(vcpu, vmcs12);
5118+
offset = get_vmcs12_field_offset(field);
5119+
if (offset < 0)
5120+
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
5121+
5122+
if (!is_guest_mode(vcpu) && is_vmcs12_ext_field(field))
5123+
copy_vmcs02_to_vmcs12_rare(vcpu, vmcs12);
5124+
5125+
/* Read the field, zero-extended to a u64 value */
5126+
value = vmcs12_read_any(vmcs12, field, offset);
5127+
} else {
5128+
/*
5129+
* Hyper-V TLFS (as of 6.0b) explicitly states, that while an
5130+
* enlightened VMCS is active VMREAD/VMWRITE instructions are
5131+
* unsupported. Unfortunately, certain versions of Windows 11
5132+
* don't comply with this requirement which is not enforced in
5133+
* genuine Hyper-V. Allow VMREAD from an enlightened VMCS as a
5134+
* workaround, as misbehaving guests will panic on VM-Fail.
5135+
* Note, enlightened VMCS is incompatible with shadow VMCS so
5136+
* all VMREADs from L2 should go to L1.
5137+
*/
5138+
if (WARN_ON_ONCE(is_guest_mode(vcpu)))
5139+
return nested_vmx_failInvalid(vcpu);
51225140

5123-
/* Read the field, zero-extended to a u64 value */
5124-
value = vmcs12_read_any(vmcs12, field, offset);
5141+
offset = evmcs_field_offset(field, NULL);
5142+
if (offset < 0)
5143+
return nested_vmx_fail(vcpu, VMXERR_UNSUPPORTED_VMCS_COMPONENT);
5144+
5145+
/* Read the field, zero-extended to a u64 value */
5146+
value = evmcs_read_any(vmx->nested.hv_evmcs, field, offset);
5147+
}
51255148

51265149
/*
51275150
* Now copy part of this value to register or memory, as requested.

0 commit comments

Comments
 (0)