Skip to content

Commit 8131cf5

Browse files
yamahatabonzini
authored andcommitted
KVM: VMX: Introduce test mode related to EPT violation VE
To support TDX, KVM is enhanced to operate with #VE. For TDX, KVM uses the suppress #VE bit in EPT entries selectively, in order to be able to trap non-present conditions. However, #VE isn't used for VMX and it's a bug if it happens. To be defensive and test that VMX case isn't broken introduce an option ept_violation_ve_test and when it's set, BUG the vm. Suggested-by: Paolo Bonzini <[email protected]> Signed-off-by: Isaku Yamahata <[email protected]> Message-Id: <d6db6ba836605c0412e166359ba5c46a63c22f86.1705965635.git.isaku.yamahata@intel.com> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent fb29541 commit 8131cf5

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

arch/x86/kvm/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ config KVM_INTEL
9595
To compile this as a module, choose M here: the module
9696
will be called kvm-intel.
9797

98+
config KVM_INTEL_PROVE_VE
99+
bool "Check that guests do not receive #VE exceptions"
100+
default KVM_PROVE_MMU || DEBUG_KERNEL
101+
depends on KVM_INTEL
102+
help
103+
104+
Checks that KVM's page table management code will not incorrectly
105+
let guests receive a virtualization exception. Virtualization
106+
exceptions will be trapped by the hypervisor rather than injected
107+
in the guest.
108+
109+
If unsure, say N.
110+
98111
config X86_SGX_KVM
99112
bool "Software Guard eXtensions (SGX) Virtualization"
100113
depends on X86_SGX && KVM_INTEL

arch/x86/kvm/vmx/vmcs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ static inline bool is_nm_fault(u32 intr_info)
140140
return is_exception_n(intr_info, NM_VECTOR);
141141
}
142142

143+
static inline bool is_ve_fault(u32 intr_info)
144+
{
145+
return is_exception_n(intr_info, VE_VECTOR);
146+
}
147+
143148
/* Undocumented: icebp/int1 */
144149
static inline bool is_icebp(u32 intr_info)
145150
{

arch/x86/kvm/vmx/vmx.c

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,12 @@ void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu)
869869

870870
eb = (1u << PF_VECTOR) | (1u << UD_VECTOR) | (1u << MC_VECTOR) |
871871
(1u << DB_VECTOR) | (1u << AC_VECTOR);
872+
/*
873+
* #VE isn't used for VMX. To test against unexpected changes
874+
* related to #VE for VMX, intercept unexpected #VE and warn on it.
875+
*/
876+
if (IS_ENABLED(CONFIG_KVM_INTEL_PROVE_VE))
877+
eb |= 1u << VE_VECTOR;
872878
/*
873879
* Guest access to VMware backdoor ports could legitimately
874880
* trigger #GP because of TSS I/O permission bitmap.
@@ -2602,6 +2608,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
26022608
&_cpu_based_2nd_exec_control))
26032609
return -EIO;
26042610
}
2611+
if (!IS_ENABLED(CONFIG_KVM_INTEL_PROVE_VE))
2612+
_cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_EPT_VIOLATION_VE;
2613+
26052614
#ifndef CONFIG_X86_64
26062615
if (!(_cpu_based_2nd_exec_control &
26072616
SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES))
@@ -2626,6 +2635,7 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
26262635
return -EIO;
26272636

26282637
vmx_cap->ept = 0;
2638+
_cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_EPT_VIOLATION_VE;
26292639
}
26302640
if (!(_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_VPID) &&
26312641
vmx_cap->vpid) {
@@ -4588,6 +4598,7 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
45884598
exec_control &= ~SECONDARY_EXEC_ENABLE_VPID;
45894599
if (!enable_ept) {
45904600
exec_control &= ~SECONDARY_EXEC_ENABLE_EPT;
4601+
exec_control &= ~SECONDARY_EXEC_EPT_VIOLATION_VE;
45914602
enable_unrestricted_guest = 0;
45924603
}
45934604
if (!enable_unrestricted_guest)
@@ -4711,8 +4722,12 @@ static void init_vmcs(struct vcpu_vmx *vmx)
47114722

47124723
exec_controls_set(vmx, vmx_exec_control(vmx));
47134724

4714-
if (cpu_has_secondary_exec_ctrls())
4725+
if (cpu_has_secondary_exec_ctrls()) {
47154726
secondary_exec_controls_set(vmx, vmx_secondary_exec_control(vmx));
4727+
if (vmx->ve_info)
4728+
vmcs_write64(VE_INFORMATION_ADDRESS,
4729+
__pa(vmx->ve_info));
4730+
}
47164731

47174732
if (cpu_has_tertiary_exec_ctrls())
47184733
tertiary_exec_controls_set(vmx, vmx_tertiary_exec_control(vmx));
@@ -5200,6 +5215,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
52005215
if (is_invalid_opcode(intr_info))
52015216
return handle_ud(vcpu);
52025217

5218+
if (KVM_BUG_ON(is_ve_fault(intr_info), vcpu->kvm))
5219+
return -EIO;
5220+
52035221
error_code = 0;
52045222
if (intr_info & INTR_INFO_DELIVER_CODE_MASK)
52055223
error_code = vmcs_read32(VM_EXIT_INTR_ERROR_CODE);
@@ -6409,8 +6427,22 @@ void dump_vmcs(struct kvm_vcpu *vcpu)
64096427
pr_err("Virtual processor ID = 0x%04x\n",
64106428
vmcs_read16(VIRTUAL_PROCESSOR_ID));
64116429
if (secondary_exec_control & SECONDARY_EXEC_EPT_VIOLATION_VE) {
6412-
pr_err("VE info address = 0x%016llx\n",
6413-
vmcs_read64(VE_INFORMATION_ADDRESS));
6430+
struct vmx_ve_information *ve_info = vmx->ve_info;
6431+
u64 ve_info_pa = vmcs_read64(VE_INFORMATION_ADDRESS);
6432+
6433+
/*
6434+
* If KVM is dumping the VMCS, then something has gone wrong
6435+
* already. Derefencing an address from the VMCS, which could
6436+
* very well be corrupted, is a terrible idea. The virtual
6437+
* address is known so use it.
6438+
*/
6439+
pr_err("VE info address = 0x%016llx%s\n", ve_info_pa,
6440+
ve_info_pa == __pa(ve_info) ? "" : "(corrupted!)");
6441+
pr_err("ve_info: 0x%08x 0x%08x 0x%016llx 0x%016llx 0x%016llx 0x%04x\n",
6442+
ve_info->exit_reason, ve_info->delivery,
6443+
ve_info->exit_qualification,
6444+
ve_info->guest_linear_address,
6445+
ve_info->guest_physical_address, ve_info->eptp_index);
64146446
}
64156447
}
64166448

@@ -7466,6 +7498,7 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu)
74667498
free_vpid(vmx->vpid);
74677499
nested_vmx_free_vcpu(vcpu);
74687500
free_loaded_vmcs(vmx->loaded_vmcs);
7501+
free_page((unsigned long)vmx->ve_info);
74697502
}
74707503

74717504
int vmx_vcpu_create(struct kvm_vcpu *vcpu)
@@ -7559,6 +7592,20 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)
75597592
goto free_vmcs;
75607593
}
75617594

7595+
err = -ENOMEM;
7596+
if (vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_EPT_VIOLATION_VE) {
7597+
struct page *page;
7598+
7599+
BUILD_BUG_ON(sizeof(*vmx->ve_info) > PAGE_SIZE);
7600+
7601+
/* ve_info must be page aligned. */
7602+
page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
7603+
if (!page)
7604+
goto free_vmcs;
7605+
7606+
vmx->ve_info = page_to_virt(page);
7607+
}
7608+
75627609
if (vmx_can_use_ipiv(vcpu))
75637610
WRITE_ONCE(to_kvm_vmx(vcpu->kvm)->pid_table[vcpu->vcpu_id],
75647611
__pa(&vmx->pi_desc) | PID_TABLE_ENTRY_VALID);

arch/x86/kvm/vmx/vmx.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ struct vcpu_vmx {
362362
DECLARE_BITMAP(read, MAX_POSSIBLE_PASSTHROUGH_MSRS);
363363
DECLARE_BITMAP(write, MAX_POSSIBLE_PASSTHROUGH_MSRS);
364364
} shadow_msr_intercept;
365+
366+
/* ve_info must be page aligned. */
367+
struct vmx_ve_information *ve_info;
365368
};
366369

367370
struct kvm_vmx {
@@ -574,7 +577,8 @@ static inline u8 vmx_get_rvi(void)
574577
SECONDARY_EXEC_ENABLE_VMFUNC | \
575578
SECONDARY_EXEC_BUS_LOCK_DETECTION | \
576579
SECONDARY_EXEC_NOTIFY_VM_EXITING | \
577-
SECONDARY_EXEC_ENCLS_EXITING)
580+
SECONDARY_EXEC_ENCLS_EXITING | \
581+
SECONDARY_EXEC_EPT_VIOLATION_VE)
578582

579583
#define KVM_REQUIRED_VMX_TERTIARY_VM_EXEC_CONTROL 0
580584
#define KVM_OPTIONAL_VMX_TERTIARY_VM_EXEC_CONTROL \

0 commit comments

Comments
 (0)