In preparation for having a separate minimal #VMEXIT path for handling failed VMRUNs, move the minimal logic out of nested_svm_vmexit() into a helper.
This includes clearing the GIF, handling single-stepping on VMRUN, and a few data structure cleanups. Basically, everything that is required by the architecture (or KVM) on a #VMEXIT where L2 never actually ran.
Additionally move uninitializing the nested MMU and reloading host CR3 to the new helper. It is not required at this point, but following changes will require it.
No functional change intended.
Cc: stable@vger.kernel.org Signed-off-by: Yosry Ahmed yosry.ahmed@linux.dev --- arch/x86/kvm/svm/nested.c | 61 ++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 27 deletions(-)
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 4781acfa3504..1356bd6383ca 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -929,6 +929,34 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, return 0; }
+static void __nested_svm_vmexit(struct vcpu_svm *svm) +{ + struct vmcb *vmcb01 = svm->vmcb01.ptr; + struct kvm_vcpu *vcpu = &svm->vcpu; + + svm->nested.vmcb12_gpa = 0; + svm->nested.ctl.nested_cr3 = 0; + + /* GIF is cleared on #VMEXIT, no event can be injected in L1 */ + svm_set_gif(svm, false); + vmcb01->control.exit_int_info = 0; + + nested_svm_uninit_mmu_context(vcpu); + if (nested_svm_load_cr3(vcpu, vmcb01->save.cr3, false, true)) { + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); + return; + } + + /* + * If we are here following the completion of a VMRUN that + * is being single-stepped, queue the pending #DB intercept + * right now so that it an be accounted for before we execute + * L1's next instruction. + */ + if (unlikely(vmcb01->save.rflags & X86_EFLAGS_TF)) + kvm_queue_exception(vcpu, DB_VECTOR); +} + int nested_svm_vmrun(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1078,8 +1106,6 @@ void nested_svm_vmexit(struct vcpu_svm *svm) /* in case we halted in L2 */ kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
- svm->nested.vmcb12_gpa = 0; - if (kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map)) { kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); return; @@ -1194,13 +1220,6 @@ void nested_svm_vmexit(struct vcpu_svm *svm) } }
- /* - * On vmexit the GIF is set to false and - * no event can be injected in L1. - */ - svm_set_gif(svm, false); - vmcb01->control.exit_int_info = 0; - svm->vcpu.arch.tsc_offset = svm->vcpu.arch.l1_tsc_offset; if (vmcb01->control.tsc_offset != svm->vcpu.arch.tsc_offset) { vmcb01->control.tsc_offset = svm->vcpu.arch.tsc_offset; @@ -1213,8 +1232,6 @@ void nested_svm_vmexit(struct vcpu_svm *svm) svm_write_tsc_multiplier(vcpu); }
- svm->nested.ctl.nested_cr3 = 0; - /* * Restore processor state that had been saved in vmcb01 */ @@ -1240,13 +1257,6 @@ void nested_svm_vmexit(struct vcpu_svm *svm)
nested_svm_transition_tlb_flush(vcpu);
- nested_svm_uninit_mmu_context(vcpu); - - if (nested_svm_load_cr3(vcpu, vmcb01->save.cr3, false, true)) { - kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); - return; - } - /* * Drop what we picked up for L2 via svm_complete_interrupts() so it * doesn't end up in L1. @@ -1255,21 +1265,18 @@ void nested_svm_vmexit(struct vcpu_svm *svm) kvm_clear_exception_queue(vcpu); kvm_clear_interrupt_queue(vcpu);
- /* - * If we are here following the completion of a VMRUN that - * is being single-stepped, queue the pending #DB intercept - * right now so that it an be accounted for before we execute - * L1's next instruction. - */ - if (unlikely(vmcb01->save.rflags & X86_EFLAGS_TF)) - kvm_queue_exception(&(svm->vcpu), DB_VECTOR); - /* * Un-inhibit the AVIC right away, so that other vCPUs can start * to benefit from it right away. */ if (kvm_apicv_activated(vcpu->kvm)) __kvm_vcpu_update_apicv(vcpu); + + /* + * Potentially queues an exception, so it needs to be after + * kvm_clear_exception_queue() is called above. + */ + __nested_svm_vmexit(svm); }
static void nested_svm_triple_fault(struct kvm_vcpu *vcpu)