Skip to content

Commit

Permalink
kvm: x86: MMU support for EPT accessed/dirty bits
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Paolo Bonzini authored and Radim Krčmář committed Apr 7, 2017
1 parent 0047723 commit 86407bc
Showing 1 changed file with 15 additions and 8 deletions.
23 changes: 15 additions & 8 deletions arch/x86/kvm/paging_tmpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -312,7 +318,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
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;

Expand Down Expand Up @@ -394,7 +400,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
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.
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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

0 comments on commit 86407bc

Please sign in to comment.