Commit 86407bcb authored by Paolo Bonzini's avatar Paolo Bonzini Committed by Radim Krčmář

kvm: x86: MMU support for EPT accessed/dirty bits

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: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
parent 00477231
...@@ -43,6 +43,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void) ...@@ -43,6 +43,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
#define PT_HAVE_ACCESSED_DIRTY(mmu) true
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
#define PT_MAX_FULL_LEVELS 4 #define PT_MAX_FULL_LEVELS 4
#define CMPXCHG cmpxchg #define CMPXCHG cmpxchg
...@@ -64,6 +65,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void) ...@@ -64,6 +65,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
#define PT_HAVE_ACCESSED_DIRTY(mmu) true
#define CMPXCHG cmpxchg #define CMPXCHG cmpxchg
#elif PTTYPE == PTTYPE_EPT #elif PTTYPE == PTTYPE_EPT
#define pt_element_t u64 #define pt_element_t u64
...@@ -78,6 +80,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void) ...@@ -78,6 +80,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK 0 #define PT_GUEST_DIRTY_MASK 0
#define PT_GUEST_DIRTY_SHIFT __using_nonexistent_pte_bit() #define PT_GUEST_DIRTY_SHIFT __using_nonexistent_pte_bit()
#define PT_GUEST_ACCESSED_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 CMPXCHG cmpxchg64
#define PT_MAX_FULL_LEVELS 4 #define PT_MAX_FULL_LEVELS 4
#else #else
...@@ -111,12 +114,13 @@ static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl) ...@@ -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; 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; unsigned mask;
/* dirty bit is not supported, so no need to track it */ /* dirty bit is not supported, so no need to track it */
if (!PT_GUEST_DIRTY_MASK) if (!PT_HAVE_ACCESSED_DIRTY(mmu))
return; return;
BUILD_BUG_ON(PT_WRITABLE_MASK != ACC_WRITE_MASK); BUILD_BUG_ON(PT_WRITABLE_MASK != ACC_WRITE_MASK);
...@@ -171,7 +175,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, ...@@ -171,7 +175,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
goto no_present; goto no_present;
/* if accessed bit is not supported prefetch non accessed gpte */ /* 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; goto no_present;
return false; return false;
...@@ -217,7 +221,7 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu, ...@@ -217,7 +221,7 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu,
int ret; int ret;
/* dirty/accessed bits are not supported, so no need to update them */ /* 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; return 0;
for (level = walker->max_level; level >= walker->level; --level) { for (level = walker->max_level; level >= walker->level; --level) {
...@@ -287,6 +291,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, ...@@ -287,6 +291,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
gfn_t table_gfn; gfn_t table_gfn;
unsigned index, pt_access, pte_access, accessed_dirty, pte_pkey; unsigned index, pt_access, pte_access, accessed_dirty, pte_pkey;
gpa_t pte_gpa; gpa_t pte_gpa;
bool have_ad;
int offset; int offset;
const int write_fault = access & PFERR_WRITE_MASK; const int write_fault = access & PFERR_WRITE_MASK;
const int user_fault = access & PFERR_USER_MASK; const int user_fault = access & PFERR_USER_MASK;
...@@ -299,6 +304,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, ...@@ -299,6 +304,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
retry_walk: retry_walk:
walker->level = mmu->root_level; walker->level = mmu->root_level;
pte = mmu->get_cr3(vcpu); pte = mmu->get_cr3(vcpu);
have_ad = PT_HAVE_ACCESSED_DIRTY(mmu);
#if PTTYPE == 64 #if PTTYPE == 64
if (walker->level == PT32E_ROOT_LEVEL) { if (walker->level == PT32E_ROOT_LEVEL) {
...@@ -312,7 +318,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, ...@@ -312,7 +318,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->max_level = walker->level; walker->max_level = walker->level;
ASSERT(!(is_long_mode(vcpu) && !is_pae(vcpu))); 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; pt_access = pte_access = ACC_ALL;
++walker->level; ++walker->level;
...@@ -394,7 +400,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, ...@@ -394,7 +400,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->gfn = real_gpa >> PAGE_SHIFT; walker->gfn = real_gpa >> PAGE_SHIFT;
if (!write_fault) if (!write_fault)
FNAME(protect_clean_gpte)(&pte_access, pte); FNAME(protect_clean_gpte)(mmu, &pte_access, pte);
else else
/* /*
* On a write fault, fold the dirty bit into accessed_dirty. * 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, ...@@ -485,7 +491,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
gfn = gpte_to_gfn(gpte); gfn = gpte_to_gfn(gpte);
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, 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, pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn,
no_dirty_log && (pte_access & ACC_WRITE_MASK)); no_dirty_log && (pte_access & ACC_WRITE_MASK));
if (is_error_pfn(pfn)) if (is_error_pfn(pfn))
...@@ -979,7 +985,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) ...@@ -979,7 +985,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
gfn = gpte_to_gfn(gpte); gfn = gpte_to_gfn(gpte);
pte_access = sp->role.access; pte_access = sp->role.access;
pte_access &= FNAME(gpte_access)(vcpu, gpte); 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, if (sync_mmio_spte(vcpu, &sp->spt[i], gfn, pte_access,
&nr_present)) &nr_present))
...@@ -1025,3 +1031,4 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) ...@@ -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_MASK
#undef PT_GUEST_DIRTY_SHIFT #undef PT_GUEST_DIRTY_SHIFT
#undef PT_GUEST_ACCESSED_SHIFT #undef PT_GUEST_ACCESSED_SHIFT
#undef PT_HAVE_ACCESSED_DIRTY
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment