kvm: x86: MMU support for EPT accessed/dirty bits
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 30 Mar 2017 09:55:29 +0000 (11:55 +0200)
committerRadim Krčmář <rkrcmar@redhat.com>
Fri, 7 Apr 2017 14:49:00 +0000 (16:49 +0200)
This prepares the MMU paging code for EPT accessed and dirty bits,
which can be enabled optionally at runtime.  Code that updates the
accessed and dirty bits will need a pointer to the struct kvm_mmu.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
arch/x86/kvm/paging_tmpl.h

index a01105485315ab56faffb79489f2eb55ba117c29..3e20f7b33892c2fd228089f8f07cc1403fe91f28 100644 (file)
@@ -43,6 +43,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
        #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
        #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
        #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
+       #define PT_HAVE_ACCESSED_DIRTY(mmu) true
        #ifdef CONFIG_X86_64
        #define PT_MAX_FULL_LEVELS 4
        #define CMPXCHG cmpxchg
@@ -64,6 +65,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
        #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
        #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
        #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
+       #define PT_HAVE_ACCESSED_DIRTY(mmu) true
        #define CMPXCHG cmpxchg
 #elif PTTYPE == PTTYPE_EPT
        #define pt_element_t u64
@@ -78,6 +80,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
        #define PT_GUEST_DIRTY_MASK 0
        #define PT_GUEST_DIRTY_SHIFT __using_nonexistent_pte_bit()
        #define PT_GUEST_ACCESSED_SHIFT __using_nonexistent_pte_bit()
+       #define PT_HAVE_ACCESSED_DIRTY(mmu) false
        #define CMPXCHG cmpxchg64
        #define PT_MAX_FULL_LEVELS 4
 #else
@@ -111,12 +114,13 @@ static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl)
        return (gpte & PT_LVL_ADDR_MASK(lvl)) >> PAGE_SHIFT;
 }
 
-static inline void FNAME(protect_clean_gpte)(unsigned *access, unsigned gpte)
+static inline void FNAME(protect_clean_gpte)(struct kvm_mmu *mmu, unsigned *access,
+                                            unsigned gpte)
 {
        unsigned mask;
 
        /* dirty bit is not supported, so no need to track it */
-       if (!PT_GUEST_DIRTY_MASK)
+       if (!PT_HAVE_ACCESSED_DIRTY(mmu))
                return;
 
        BUILD_BUG_ON(PT_WRITABLE_MASK != ACC_WRITE_MASK);
@@ -171,7 +175,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
                goto no_present;
 
        /* if accessed bit is not supported prefetch non accessed gpte */
-       if (PT_GUEST_ACCESSED_MASK && !(gpte & PT_GUEST_ACCESSED_MASK))
+       if (PT_HAVE_ACCESSED_DIRTY(&vcpu->arch.mmu) && !(gpte & PT_GUEST_ACCESSED_MASK))
                goto no_present;
 
        return false;
@@ -217,7 +221,7 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu,
        int ret;
 
        /* dirty/accessed bits are not supported, so no need to update them */
-       if (!PT_GUEST_DIRTY_MASK)
+       if (!PT_HAVE_ACCESSED_DIRTY(mmu))
                return 0;
 
        for (level = walker->max_level; level >= walker->level; --level) {
@@ -287,6 +291,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
        gfn_t table_gfn;
        unsigned index, pt_access, pte_access, accessed_dirty, pte_pkey;
        gpa_t pte_gpa;
+       bool have_ad;
        int offset;
        const int write_fault = access & PFERR_WRITE_MASK;
        const int user_fault  = access & PFERR_USER_MASK;
@@ -299,6 +304,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
 retry_walk:
        walker->level = mmu->root_level;
        pte           = mmu->get_cr3(vcpu);
+       have_ad       = PT_HAVE_ACCESSED_DIRTY(mmu);
 
 #if PTTYPE == 64
        if (walker->level == PT32E_ROOT_LEVEL) {
@@ -312,7 +318,7 @@ retry_walk:
        walker->max_level = walker->level;
        ASSERT(!(is_long_mode(vcpu) && !is_pae(vcpu)));
 
-       accessed_dirty = PT_GUEST_ACCESSED_MASK;
+       accessed_dirty = have_ad ? PT_GUEST_ACCESSED_MASK : 0;
        pt_access = pte_access = ACC_ALL;
        ++walker->level;
 
@@ -394,7 +400,7 @@ retry_walk:
        walker->gfn = real_gpa >> PAGE_SHIFT;
 
        if (!write_fault)
-               FNAME(protect_clean_gpte)(&pte_access, pte);
+               FNAME(protect_clean_gpte)(mmu, &pte_access, pte);
        else
                /*
                 * On a write fault, fold the dirty bit into accessed_dirty.
@@ -485,7 +491,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
 
        gfn = gpte_to_gfn(gpte);
        pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
-       FNAME(protect_clean_gpte)(&pte_access, gpte);
+       FNAME(protect_clean_gpte)(&vcpu->arch.mmu, &pte_access, gpte);
        pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn,
                        no_dirty_log && (pte_access & ACC_WRITE_MASK));
        if (is_error_pfn(pfn))
@@ -979,7 +985,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
                gfn = gpte_to_gfn(gpte);
                pte_access = sp->role.access;
                pte_access &= FNAME(gpte_access)(vcpu, gpte);
-               FNAME(protect_clean_gpte)(&pte_access, gpte);
+               FNAME(protect_clean_gpte)(&vcpu->arch.mmu, &pte_access, gpte);
 
                if (sync_mmio_spte(vcpu, &sp->spt[i], gfn, pte_access,
                      &nr_present))
@@ -1025,3 +1031,4 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
 #undef PT_GUEST_DIRTY_MASK
 #undef PT_GUEST_DIRTY_SHIFT
 #undef PT_GUEST_ACCESSED_SHIFT
+#undef PT_HAVE_ACCESSED_DIRTY