Skip to content

Commit 8ea667f

Browse files
committed
KVM: MMU: Push clean gpte write protection out of gpte_access()
gpte_access() computes the access permissions of a guest pte and also write-protects clean gptes. This is wrong when we are servicing a write fault (since we'll be setting the dirty bit momentarily) but correct when instantiating a speculative spte, or when servicing a read fault (since we'll want to trap a following write in order to set the dirty bit). It doesn't seem to hurt in practice, but in order to make the code readable, push the write protection out of gpte_access() and into a new protect_clean_gpte() which is called explicitly when needed. Reviewed-by: Xiao Guangrong <[email protected]> Signed-off-by: Avi Kivity <[email protected]>
1 parent 879238f commit 8ea667f

File tree

3 files changed

+26
-13
lines changed

3 files changed

+26
-13
lines changed

arch/x86/kvm/mmu.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,6 +3408,18 @@ static bool is_rsvd_bits_set(struct kvm_mmu *mmu, u64 gpte, int level)
34083408
return (gpte & mmu->rsvd_bits_mask[bit7][level-1]) != 0;
34093409
}
34103410

3411+
static inline void protect_clean_gpte(unsigned *access, unsigned gpte)
3412+
{
3413+
unsigned mask;
3414+
3415+
BUILD_BUG_ON(PT_WRITABLE_MASK != ACC_WRITE_MASK);
3416+
3417+
mask = (unsigned)~ACC_WRITE_MASK;
3418+
/* Allow write access to dirty gptes */
3419+
mask |= (gpte >> (PT_DIRTY_SHIFT - PT_WRITABLE_SHIFT)) & PT_WRITABLE_MASK;
3420+
*access &= mask;
3421+
}
3422+
34113423
static bool sync_mmio_spte(u64 *sptep, gfn_t gfn, unsigned access,
34123424
int *nr_present)
34133425
{

arch/x86/kvm/mmu.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
#define PT_PCD_MASK (1ULL << 4)
1919
#define PT_ACCESSED_SHIFT 5
2020
#define PT_ACCESSED_MASK (1ULL << PT_ACCESSED_SHIFT)
21-
#define PT_DIRTY_MASK (1ULL << 6)
21+
#define PT_DIRTY_SHIFT 6
22+
#define PT_DIRTY_MASK (1ULL << PT_DIRTY_SHIFT)
2223
#define PT_PAGE_SIZE_MASK (1ULL << 7)
2324
#define PT_PAT_MASK (1ULL << 7)
2425
#define PT_GLOBAL_MASK (1ULL << 8)

arch/x86/kvm/paging_tmpl.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,11 @@ static int FNAME(cmpxchg_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
101101
return (ret != orig_pte);
102102
}
103103

104-
static unsigned FNAME(gpte_access)(struct kvm_vcpu *vcpu, pt_element_t gpte,
105-
bool last)
104+
static unsigned FNAME(gpte_access)(struct kvm_vcpu *vcpu, pt_element_t gpte)
106105
{
107106
unsigned access;
108107

109108
access = (gpte & (PT_WRITABLE_MASK | PT_USER_MASK)) | ACC_EXEC_MASK;
110-
if (last && !is_dirty_gpte(gpte))
111-
access &= ~ACC_WRITE_MASK;
112109

113110
#if PTTYPE == 64
114111
if (vcpu->arch.mmu.nx)
@@ -222,8 +219,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
222219

223220
last_gpte = FNAME(is_last_gpte)(walker, vcpu, mmu, pte);
224221
if (last_gpte) {
225-
pte_access = pt_access &
226-
FNAME(gpte_access)(vcpu, pte, true);
222+
pte_access = pt_access & FNAME(gpte_access)(vcpu, pte);
227223
/* check if the kernel is fetching from user page */
228224
if (unlikely(pte_access & PT_USER_MASK) &&
229225
kvm_read_cr4_bits(vcpu, X86_CR4_SMEP))
@@ -274,7 +270,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
274270
break;
275271
}
276272

277-
pt_access &= FNAME(gpte_access)(vcpu, pte, false);
273+
pt_access &= FNAME(gpte_access)(vcpu, pte);
278274
--walker->level;
279275
}
280276

@@ -283,7 +279,9 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
283279
goto error;
284280
}
285281

286-
if (write_fault && unlikely(!is_dirty_gpte(pte))) {
282+
if (!write_fault)
283+
protect_clean_gpte(&pte_access, pte);
284+
else if (unlikely(!is_dirty_gpte(pte))) {
287285
int ret;
288286

289287
trace_kvm_mmu_set_dirty_bit(table_gfn, index, sizeof(pte));
@@ -368,7 +366,8 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
368366
return;
369367

370368
pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte);
371-
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte, true);
369+
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
370+
protect_clean_gpte(&pte_access, gpte);
372371
pfn = gfn_to_pfn_atomic(vcpu->kvm, gpte_to_gfn(gpte));
373372
if (mmu_invalid_pfn(pfn))
374373
return;
@@ -441,8 +440,8 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
441440
if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte))
442441
continue;
443442

444-
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte,
445-
true);
443+
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
444+
protect_clean_gpte(&pte_access, gpte);
446445
gfn = gpte_to_gfn(gpte);
447446
pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn,
448447
pte_access & ACC_WRITE_MASK);
@@ -794,7 +793,8 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
794793

795794
gfn = gpte_to_gfn(gpte);
796795
pte_access = sp->role.access;
797-
pte_access &= FNAME(gpte_access)(vcpu, gpte, true);
796+
pte_access &= FNAME(gpte_access)(vcpu, gpte);
797+
protect_clean_gpte(&pte_access, gpte);
798798

799799
if (sync_mmio_spte(&sp->spt[i], gfn, pte_access, &nr_present))
800800
continue;

0 commit comments

Comments
 (0)