When emulating INVVPID for L1, free only L2+ roots, using the guest_mode
tag in the MMU role to identify L2+ roots.  From L1's perspective, its
own TLB entries use VPID=0, and INVVPID is not requied to invalidate such
entries.  Per Intel's SDM, INVVPID _may_ invalidate entries with VPID=0,
but it is not required to do so.
Cc: Lai Jiangshan <laijs@linux.alibaba.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <
20210609234235.
1244004-10-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
 void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
 void kvm_mmu_free_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
                        ulong roots_to_free);
+void kvm_mmu_free_guest_mode_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu);
 gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
                           struct x86_exception *exception);
 gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva,
 
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_free_roots);
 
+void kvm_mmu_free_guest_mode_roots(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu)
+{
+       unsigned long roots_to_free = 0;
+       hpa_t root_hpa;
+       int i;
+
+       /*
+        * This should not be called while L2 is active, L2 can't invalidate
+        * _only_ its own roots, e.g. INVVPID unconditionally exits.
+        */
+       WARN_ON_ONCE(mmu->mmu_role.base.guest_mode);
+
+       for (i = 0; i < KVM_MMU_NUM_PREV_ROOTS; i++) {
+               root_hpa = mmu->prev_roots[i].hpa;
+               if (!VALID_PAGE(root_hpa))
+                       continue;
+
+               if (!to_shadow_page(root_hpa) ||
+                       to_shadow_page(root_hpa)->role.guest_mode)
+                       roots_to_free |= KVM_MMU_ROOT_PREVIOUS(i);
+       }
+
+       kvm_mmu_free_roots(vcpu, mmu, roots_to_free);
+}
+EXPORT_SYMBOL_GPL(kvm_mmu_free_guest_mode_roots);
+
+
 static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn)
 {
        int ret = 0;
 
 
        /*
         * Sync the shadow page tables if EPT is disabled, L1 is invalidating
-        * linear mappings for L2 (tagged with L2's VPID).  Free all roots as
-        * VPIDs are not tracked in the MMU role.
+        * linear mappings for L2 (tagged with L2's VPID).  Free all guest
+        * roots as VPIDs are not tracked in the MMU role.
         *
         * Note, this operates on root_mmu, not guest_mmu, as L1 and L2 share
         * an MMU when EPT is disabled.
         * TODO: sync only the affected SPTEs for INVDIVIDUAL_ADDR.
         */
        if (!enable_ept)
-               kvm_mmu_free_roots(vcpu, &vcpu->arch.root_mmu,
-                                  KVM_MMU_ROOTS_ALL);
+               kvm_mmu_free_guest_mode_roots(vcpu, &vcpu->arch.root_mmu);
 
        return nested_vmx_succeed(vcpu);
 }