KVM: MMU: mode specific sync_page
Examine guest pagetable and bring the shadow back in sync. Caller is responsible for local TLB flush before re-entering guest mode. Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
committed by
Avi Kivity
parent
38187c830c
commit
e8bc217aef
@@ -507,6 +507,60 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Using the cached information from sp->gfns is safe because:
|
||||
* - The spte has a reference to the struct page, so the pfn for a given gfn
|
||||
* can't change unless all sptes pointing to it are nuked first.
|
||||
* - Alias changes zap the entire shadow cache.
|
||||
*/
|
||||
static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||
{
|
||||
int i, offset, nr_present;
|
||||
|
||||
offset = nr_present = 0;
|
||||
|
||||
if (PTTYPE == 32)
|
||||
offset = sp->role.quadrant << PT64_LEVEL_BITS;
|
||||
|
||||
for (i = 0; i < PT64_ENT_PER_PAGE; i++) {
|
||||
unsigned pte_access;
|
||||
pt_element_t gpte;
|
||||
gpa_t pte_gpa;
|
||||
gfn_t gfn = sp->gfns[i];
|
||||
|
||||
if (!is_shadow_present_pte(sp->spt[i]))
|
||||
continue;
|
||||
|
||||
pte_gpa = gfn_to_gpa(sp->gfn);
|
||||
pte_gpa += (i+offset) * sizeof(pt_element_t);
|
||||
|
||||
if (kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &gpte,
|
||||
sizeof(pt_element_t)))
|
||||
return -EINVAL;
|
||||
|
||||
if (gpte_to_gfn(gpte) != gfn || !is_present_pte(gpte) ||
|
||||
!(gpte & PT_ACCESSED_MASK)) {
|
||||
u64 nonpresent;
|
||||
|
||||
rmap_remove(vcpu->kvm, &sp->spt[i]);
|
||||
if (is_present_pte(gpte))
|
||||
nonpresent = shadow_trap_nonpresent_pte;
|
||||
else
|
||||
nonpresent = shadow_notrap_nonpresent_pte;
|
||||
set_shadow_pte(&sp->spt[i], nonpresent);
|
||||
continue;
|
||||
}
|
||||
|
||||
nr_present++;
|
||||
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
|
||||
set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
|
||||
is_dirty_pte(gpte), 0, gfn,
|
||||
spte_to_pfn(sp->spt[i]), true);
|
||||
}
|
||||
|
||||
return !nr_present;
|
||||
}
|
||||
|
||||
#undef pt_element_t
|
||||
#undef guest_walker
|
||||
#undef shadow_walker
|
||||
|
Reference in New Issue
Block a user