Skip to content

Commit 8e53324

Browse files
Sean Christophersonbonzini
authored andcommitted
KVM: VMX: Convert vcpu_vmx.exit_reason to a union
Convert vcpu_vmx.exit_reason from a u32 to a union (of size u32). The full VM_EXIT_REASON field is comprised of a 16-bit basic exit reason in bits 15:0, and single-bit modifiers in bits 31:16. Historically, KVM has only had to worry about handling the "failed VM-Entry" modifier, which could only be set in very specific flows and required dedicated handling. I.e. manually stripping the FAILED_VMENTRY bit was a somewhat viable approach. But even with only a single bit to worry about, KVM has had several bugs related to comparing a basic exit reason against the full exit reason store in vcpu_vmx. Upcoming Intel features, e.g. SGX, will add new modifier bits that can be set on more or less any VM-Exit, as opposed to the significantly more restricted FAILED_VMENTRY, i.e. correctly handling everything in one-off flows isn't scalable. Tracking exit reason in a union forces code to explicitly choose between consuming the full exit reason and the basic exit, and is a convenient way to document and access the modifiers. No functional change intended. Cc: Xiaoyao Li <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Signed-off-by: Chenyi Qiang <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 2c07ded commit 8e53324

File tree

3 files changed

+86
-49
lines changed

3 files changed

+86
-49
lines changed

arch/x86/kvm/vmx/nested.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3330,7 +3330,11 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
33303330
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
33313331
enum vm_entry_failure_code entry_failure_code;
33323332
bool evaluate_pending_interrupts;
3333-
u32 exit_reason, failed_index;
3333+
union vmx_exit_reason exit_reason = {
3334+
.basic = EXIT_REASON_INVALID_STATE,
3335+
.failed_vmentry = 1,
3336+
};
3337+
u32 failed_index;
33343338

33353339
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
33363340
kvm_vcpu_flush_tlb_current(vcpu);
@@ -3382,7 +3386,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
33823386

33833387
if (nested_vmx_check_guest_state(vcpu, vmcs12,
33843388
&entry_failure_code)) {
3385-
exit_reason = EXIT_REASON_INVALID_STATE;
3389+
exit_reason.basic = EXIT_REASON_INVALID_STATE;
33863390
vmcs12->exit_qualification = entry_failure_code;
33873391
goto vmentry_fail_vmexit;
33883392
}
@@ -3393,7 +3397,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
33933397
vcpu->arch.tsc_offset += vmcs12->tsc_offset;
33943398

33953399
if (prepare_vmcs02(vcpu, vmcs12, &entry_failure_code)) {
3396-
exit_reason = EXIT_REASON_INVALID_STATE;
3400+
exit_reason.basic = EXIT_REASON_INVALID_STATE;
33973401
vmcs12->exit_qualification = entry_failure_code;
33983402
goto vmentry_fail_vmexit_guest_mode;
33993403
}
@@ -3403,7 +3407,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
34033407
vmcs12->vm_entry_msr_load_addr,
34043408
vmcs12->vm_entry_msr_load_count);
34053409
if (failed_index) {
3406-
exit_reason = EXIT_REASON_MSR_LOAD_FAIL;
3410+
exit_reason.basic = EXIT_REASON_MSR_LOAD_FAIL;
34073411
vmcs12->exit_qualification = failed_index;
34083412
goto vmentry_fail_vmexit_guest_mode;
34093413
}
@@ -3471,7 +3475,7 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
34713475
return NVMX_VMENTRY_VMEXIT;
34723476

34733477
load_vmcs12_host_state(vcpu, vmcs12);
3474-
vmcs12->vm_exit_reason = exit_reason | VMX_EXIT_REASONS_FAILED_VMENTRY;
3478+
vmcs12->vm_exit_reason = exit_reason.full;
34753479
if (enable_shadow_vmcs || vmx->nested.hv_evmcs)
34763480
vmx->nested.need_vmcs12_to_shadow_sync = true;
34773481
return NVMX_VMENTRY_VMEXIT;
@@ -5559,7 +5563,12 @@ static int handle_vmfunc(struct kvm_vcpu *vcpu)
55595563
return kvm_skip_emulated_instruction(vcpu);
55605564

55615565
fail:
5562-
nested_vmx_vmexit(vcpu, vmx->exit_reason,
5566+
/*
5567+
* This is effectively a reflected VM-Exit, as opposed to a synthesized
5568+
* nested VM-Exit. Pass the original exit reason, i.e. don't hardcode
5569+
* EXIT_REASON_VMFUNC as the exit reason.
5570+
*/
5571+
nested_vmx_vmexit(vcpu, vmx->exit_reason.full,
55635572
vmx_get_intr_info(vcpu),
55645573
vmx_get_exit_qual(vcpu));
55655574
return 1;
@@ -5627,7 +5636,8 @@ static bool nested_vmx_exit_handled_io(struct kvm_vcpu *vcpu,
56275636
* MSR bitmap. This may be the case even when L0 doesn't use MSR bitmaps.
56285637
*/
56295638
static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,
5630-
struct vmcs12 *vmcs12, u32 exit_reason)
5639+
struct vmcs12 *vmcs12,
5640+
union vmx_exit_reason exit_reason)
56315641
{
56325642
u32 msr_index = kvm_rcx_read(vcpu);
56335643
gpa_t bitmap;
@@ -5641,7 +5651,7 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,
56415651
* First we need to figure out which of the four to use:
56425652
*/
56435653
bitmap = vmcs12->msr_bitmap;
5644-
if (exit_reason == EXIT_REASON_MSR_WRITE)
5654+
if (exit_reason.basic == EXIT_REASON_MSR_WRITE)
56455655
bitmap += 2048;
56465656
if (msr_index >= 0xc0000000) {
56475657
msr_index -= 0xc0000000;
@@ -5778,11 +5788,12 @@ static bool nested_vmx_exit_handled_mtf(struct vmcs12 *vmcs12)
57785788
* Return true if L0 wants to handle an exit from L2 regardless of whether or not
57795789
* L1 wants the exit. Only call this when in is_guest_mode (L2).
57805790
*/
5781-
static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)
5791+
static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu,
5792+
union vmx_exit_reason exit_reason)
57825793
{
57835794
u32 intr_info;
57845795

5785-
switch ((u16)exit_reason) {
5796+
switch ((u16)exit_reason.basic) {
57865797
case EXIT_REASON_EXCEPTION_NMI:
57875798
intr_info = vmx_get_intr_info(vcpu);
57885799
if (is_nmi(intr_info))
@@ -5838,12 +5849,13 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)
58385849
* Return 1 if L1 wants to intercept an exit from L2. Only call this when in
58395850
* is_guest_mode (L2).
58405851
*/
5841-
static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)
5852+
static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu,
5853+
union vmx_exit_reason exit_reason)
58425854
{
58435855
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
58445856
u32 intr_info;
58455857

5846-
switch ((u16)exit_reason) {
5858+
switch ((u16)exit_reason.basic) {
58475859
case EXIT_REASON_EXCEPTION_NMI:
58485860
intr_info = vmx_get_intr_info(vcpu);
58495861
if (is_nmi(intr_info))
@@ -5962,7 +5974,7 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu, u32 exit_reason)
59625974
bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu)
59635975
{
59645976
struct vcpu_vmx *vmx = to_vmx(vcpu);
5965-
u32 exit_reason = vmx->exit_reason;
5977+
union vmx_exit_reason exit_reason = vmx->exit_reason;
59665978
unsigned long exit_qual;
59675979
u32 exit_intr_info;
59685980

@@ -5981,7 +5993,7 @@ bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu)
59815993
goto reflect_vmexit;
59825994
}
59835995

5984-
trace_kvm_nested_vmexit(exit_reason, vcpu, KVM_ISA_VMX);
5996+
trace_kvm_nested_vmexit(exit_reason.full, vcpu, KVM_ISA_VMX);
59855997

59865998
/* If L0 (KVM) wants the exit, it trumps L1's desires. */
59875999
if (nested_vmx_l0_wants_exit(vcpu, exit_reason))
@@ -6007,7 +6019,7 @@ bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu)
60076019
exit_qual = vmx_get_exit_qual(vcpu);
60086020

60096021
reflect_vmexit:
6010-
nested_vmx_vmexit(vcpu, exit_reason, exit_intr_info, exit_qual);
6022+
nested_vmx_vmexit(vcpu, exit_reason.full, exit_intr_info, exit_qual);
60116023
return true;
60126024
}
60136025

arch/x86/kvm/vmx/vmx.c

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,7 +1577,7 @@ static int skip_emulated_instruction(struct kvm_vcpu *vcpu)
15771577
* i.e. we end up advancing IP with some random value.
15781578
*/
15791579
if (!static_cpu_has(X86_FEATURE_HYPERVISOR) ||
1580-
to_vmx(vcpu)->exit_reason != EXIT_REASON_EPT_MISCONFIG) {
1580+
to_vmx(vcpu)->exit_reason.basic != EXIT_REASON_EPT_MISCONFIG) {
15811581
orig_rip = kvm_rip_read(vcpu);
15821582
rip = orig_rip + vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
15831583
#ifdef CONFIG_X86_64
@@ -5667,7 +5667,7 @@ static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2,
56675667
struct vcpu_vmx *vmx = to_vmx(vcpu);
56685668

56695669
*info1 = vmx_get_exit_qual(vcpu);
5670-
if (!(vmx->exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY)) {
5670+
if (!(vmx->exit_reason.failed_vmentry)) {
56715671
*info2 = vmx->idt_vectoring_info;
56725672
*intr_info = vmx_get_intr_info(vcpu);
56735673
if (is_exception_with_error_code(*intr_info))
@@ -5911,8 +5911,9 @@ void dump_vmcs(void)
59115911
static int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
59125912
{
59135913
struct vcpu_vmx *vmx = to_vmx(vcpu);
5914-
u32 exit_reason = vmx->exit_reason;
5914+
union vmx_exit_reason exit_reason = vmx->exit_reason;
59155915
u32 vectoring_info = vmx->idt_vectoring_info;
5916+
u16 exit_handler_index;
59165917

59175918
/*
59185919
* Flush logged GPAs PML buffer, this will make dirty_bitmap more
@@ -5954,11 +5955,11 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
59545955
return 1;
59555956
}
59565957

5957-
if (exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY) {
5958+
if (exit_reason.failed_vmentry) {
59585959
dump_vmcs();
59595960
vcpu->run->exit_reason = KVM_EXIT_FAIL_ENTRY;
59605961
vcpu->run->fail_entry.hardware_entry_failure_reason
5961-
= exit_reason;
5962+
= exit_reason.full;
59625963
vcpu->run->fail_entry.cpu = vcpu->arch.last_vmentry_cpu;
59635964
return 0;
59645965
}
@@ -5980,18 +5981,18 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
59805981
* will cause infinite loop.
59815982
*/
59825983
if ((vectoring_info & VECTORING_INFO_VALID_MASK) &&
5983-
(exit_reason != EXIT_REASON_EXCEPTION_NMI &&
5984-
exit_reason != EXIT_REASON_EPT_VIOLATION &&
5985-
exit_reason != EXIT_REASON_PML_FULL &&
5986-
exit_reason != EXIT_REASON_APIC_ACCESS &&
5987-
exit_reason != EXIT_REASON_TASK_SWITCH)) {
5984+
(exit_reason.basic != EXIT_REASON_EXCEPTION_NMI &&
5985+
exit_reason.basic != EXIT_REASON_EPT_VIOLATION &&
5986+
exit_reason.basic != EXIT_REASON_PML_FULL &&
5987+
exit_reason.basic != EXIT_REASON_APIC_ACCESS &&
5988+
exit_reason.basic != EXIT_REASON_TASK_SWITCH)) {
59885989
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
59895990
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_DELIVERY_EV;
59905991
vcpu->run->internal.ndata = 3;
59915992
vcpu->run->internal.data[0] = vectoring_info;
5992-
vcpu->run->internal.data[1] = exit_reason;
5993+
vcpu->run->internal.data[1] = exit_reason.full;
59935994
vcpu->run->internal.data[2] = vcpu->arch.exit_qualification;
5994-
if (exit_reason == EXIT_REASON_EPT_MISCONFIG) {
5995+
if (exit_reason.basic == EXIT_REASON_EPT_MISCONFIG) {
59955996
vcpu->run->internal.ndata++;
59965997
vcpu->run->internal.data[3] =
59975998
vmcs_read64(GUEST_PHYSICAL_ADDRESS);
@@ -6023,38 +6024,39 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
60236024
if (exit_fastpath != EXIT_FASTPATH_NONE)
60246025
return 1;
60256026

6026-
if (exit_reason >= kvm_vmx_max_exit_handlers)
6027+
if (exit_reason.basic >= kvm_vmx_max_exit_handlers)
60276028
goto unexpected_vmexit;
60286029
#ifdef CONFIG_RETPOLINE
6029-
if (exit_reason == EXIT_REASON_MSR_WRITE)
6030+
if (exit_reason.basic == EXIT_REASON_MSR_WRITE)
60306031
return kvm_emulate_wrmsr(vcpu);
6031-
else if (exit_reason == EXIT_REASON_PREEMPTION_TIMER)
6032+
else if (exit_reason.basic == EXIT_REASON_PREEMPTION_TIMER)
60326033
return handle_preemption_timer(vcpu);
6033-
else if (exit_reason == EXIT_REASON_INTERRUPT_WINDOW)
6034+
else if (exit_reason.basic == EXIT_REASON_INTERRUPT_WINDOW)
60346035
return handle_interrupt_window(vcpu);
6035-
else if (exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT)
6036+
else if (exit_reason.basic == EXIT_REASON_EXTERNAL_INTERRUPT)
60366037
return handle_external_interrupt(vcpu);
6037-
else if (exit_reason == EXIT_REASON_HLT)
6038+
else if (exit_reason.basic == EXIT_REASON_HLT)
60386039
return kvm_emulate_halt(vcpu);
6039-
else if (exit_reason == EXIT_REASON_EPT_MISCONFIG)
6040+
else if (exit_reason.basic == EXIT_REASON_EPT_MISCONFIG)
60406041
return handle_ept_misconfig(vcpu);
60416042
#endif
60426043

6043-
exit_reason = array_index_nospec(exit_reason,
6044-
kvm_vmx_max_exit_handlers);
6045-
if (!kvm_vmx_exit_handlers[exit_reason])
6044+
exit_handler_index = array_index_nospec((u16)exit_reason.basic,
6045+
kvm_vmx_max_exit_handlers);
6046+
if (!kvm_vmx_exit_handlers[exit_handler_index])
60466047
goto unexpected_vmexit;
60476048

6048-
return kvm_vmx_exit_handlers[exit_reason](vcpu);
6049+
return kvm_vmx_exit_handlers[exit_handler_index](vcpu);
60496050

60506051
unexpected_vmexit:
6051-
vcpu_unimpl(vcpu, "vmx: unexpected exit reason 0x%x\n", exit_reason);
6052+
vcpu_unimpl(vcpu, "vmx: unexpected exit reason 0x%x\n",
6053+
exit_reason.full);
60526054
dump_vmcs();
60536055
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
60546056
vcpu->run->internal.suberror =
60556057
KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON;
60566058
vcpu->run->internal.ndata = 2;
6057-
vcpu->run->internal.data[0] = exit_reason;
6059+
vcpu->run->internal.data[0] = exit_reason.full;
60586060
vcpu->run->internal.data[1] = vcpu->arch.last_vmentry_cpu;
60596061
return 0;
60606062
}
@@ -6373,9 +6375,9 @@ static void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu)
63736375
{
63746376
struct vcpu_vmx *vmx = to_vmx(vcpu);
63756377

6376-
if (vmx->exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT)
6378+
if (vmx->exit_reason.basic == EXIT_REASON_EXTERNAL_INTERRUPT)
63776379
handle_external_interrupt_irqoff(vcpu);
6378-
else if (vmx->exit_reason == EXIT_REASON_EXCEPTION_NMI)
6380+
else if (vmx->exit_reason.basic == EXIT_REASON_EXCEPTION_NMI)
63796381
handle_exception_nmi_irqoff(vmx);
63806382
}
63816383

@@ -6567,7 +6569,7 @@ void noinstr vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp)
65676569

65686570
static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
65696571
{
6570-
switch (to_vmx(vcpu)->exit_reason) {
6572+
switch (to_vmx(vcpu)->exit_reason.basic) {
65716573
case EXIT_REASON_MSR_WRITE:
65726574
return handle_fastpath_set_msr_irqoff(vcpu);
65736575
case EXIT_REASON_PREEMPTION_TIMER:
@@ -6768,17 +6770,17 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)
67686770
vmx->idt_vectoring_info = 0;
67696771

67706772
if (unlikely(vmx->fail)) {
6771-
vmx->exit_reason = 0xdead;
6773+
vmx->exit_reason.full = 0xdead;
67726774
return EXIT_FASTPATH_NONE;
67736775
}
67746776

6775-
vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
6776-
if (unlikely((u16)vmx->exit_reason == EXIT_REASON_MCE_DURING_VMENTRY))
6777+
vmx->exit_reason.full = vmcs_read32(VM_EXIT_REASON);
6778+
if (unlikely((u16)vmx->exit_reason.basic == EXIT_REASON_MCE_DURING_VMENTRY))
67776779
kvm_machine_check();
67786780

6779-
trace_kvm_exit(vmx->exit_reason, vcpu, KVM_ISA_VMX);
6781+
trace_kvm_exit(vmx->exit_reason.full, vcpu, KVM_ISA_VMX);
67806782

6781-
if (unlikely(vmx->exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY))
6783+
if (unlikely(vmx->exit_reason.failed_vmentry))
67826784
return EXIT_FASTPATH_NONE;
67836785

67846786
vmx->loaded_vmcs->launched = 1;

arch/x86/kvm/vmx/vmx.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ struct pt_desc {
7070
struct pt_ctx guest;
7171
};
7272

73+
union vmx_exit_reason {
74+
struct {
75+
u32 basic : 16;
76+
u32 reserved16 : 1;
77+
u32 reserved17 : 1;
78+
u32 reserved18 : 1;
79+
u32 reserved19 : 1;
80+
u32 reserved20 : 1;
81+
u32 reserved21 : 1;
82+
u32 reserved22 : 1;
83+
u32 reserved23 : 1;
84+
u32 reserved24 : 1;
85+
u32 reserved25 : 1;
86+
u32 reserved26 : 1;
87+
u32 enclave_mode : 1;
88+
u32 smi_pending_mtf : 1;
89+
u32 smi_from_vmx_root : 1;
90+
u32 reserved30 : 1;
91+
u32 failed_vmentry : 1;
92+
};
93+
u32 full;
94+
};
95+
7396
/*
7497
* The nested_vmx structure is part of vcpu_vmx, and holds information we need
7598
* for correct emulation of VMX (i.e., nested VMX) on this vcpu.
@@ -244,7 +267,7 @@ struct vcpu_vmx {
244267
int vpid;
245268
bool emulation_required;
246269

247-
u32 exit_reason;
270+
union vmx_exit_reason exit_reason;
248271

249272
/* Posted interrupt descriptor */
250273
struct pi_desc pi_desc;

0 commit comments

Comments
 (0)