An AVIC table invalidation is not supposed to happen often, and can only happen when the guest does something suspicious such as: - It places physid page in a memslot that is enabled/disabled and memslot flushing happens. - It tries to update apic backing page addresses - guest has no reason to touch this, and doing so on real hardware will likely result in unpredictable results. - It writes to reserved bits of a tracked page. - It write floods a physid table while no vCPU is using it (the page is likely reused at that point to contain something else) All of the above causes a KVM_REQ_APIC_PAGE_RELOAD request to be raised on all vCPUS, which kicks them out of the guest mode, and then first vCPU to reach the handler will re-create the entries of the physid page, and others will notice this and do nothing. Signed-off-by: Maxim Levitsky <mlevitsk@xxxxxxxxxx> --- arch/x86/kvm/svm/avic.c | 13 +++++++++++++ arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 15 insertions(+) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index e6ec525a88625..f13ca1e7b2845 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -379,6 +379,7 @@ static void avic_physid_shadow_table_invalidate(struct kvm *kvm, struct kvm_svm *kvm_svm = to_kvm_svm(kvm); lockdep_assert_held(&kvm_svm->avic.tables_lock); + kvm_make_all_cpus_request(kvm, KVM_REQ_APIC_PAGE_RELOAD); avic_physid_shadow_table_erase(kvm, t); } @@ -1638,3 +1639,15 @@ bool avic_nested_has_interrupt(struct kvm_vcpu *vcpu) return true; return false; } + +void avic_reload_apic_pages(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *vcpu_svm = to_svm(vcpu); + struct avic_physid_table *t = vcpu_svm->nested.l2_physical_id_table; + + int nentries = vcpu_svm->nested.ctl.avic_physical_id & + AVIC_PHYSICAL_ID_TABLE_SIZE_MASK; + + if (t && is_guest_mode(vcpu) && nested_avic_in_use(vcpu)) + avic_physid_shadow_table_sync(vcpu, t, nentries); +} diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index a39bb0b27a51d..d96a73931d1e5 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4677,6 +4677,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .enable_nmi_window = svm_enable_nmi_window, .enable_irq_window = svm_enable_irq_window, .update_cr8_intercept = svm_update_cr8_intercept, + .reload_apic_pages = avic_reload_apic_pages, .refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl, .check_apicv_inhibit_reasons = avic_check_apicv_inhibit_reasons, .apicv_post_state_restore = avic_apicv_post_state_restore, diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 17fcc09cf4be1..93fd9d6f5fd85 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -711,6 +711,7 @@ void avic_vcpu_blocking(struct kvm_vcpu *vcpu); void avic_vcpu_unblocking(struct kvm_vcpu *vcpu); void avic_ring_doorbell(struct kvm_vcpu *vcpu); unsigned long avic_vcpu_get_apicv_inhibit_reasons(struct kvm_vcpu *vcpu); +void avic_reload_apic_pages(struct kvm_vcpu *vcpu); void avic_free_nested(struct kvm_vcpu *vcpu); bool avic_nested_has_interrupt(struct kvm_vcpu *vcpu); -- 2.26.3