@@ -3108,9 +3108,6 @@ static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu,
return;
}
- if (is_rsvd_bits_set(&vcpu->arch.mmu, *(u64 *)new, PT_PAGE_TABLE_LEVEL))
- return;
-
++vcpu->kvm->stat.mmu_pte_updated;
if (!sp->role.cr4_pae)
paging32_update_pte(vcpu, sp, spte, new);
@@ -299,42 +299,90 @@ static int FNAME(walk_addr_nested)(struct guest_walker *walker,
addr, access);
}
-static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
- u64 *spte, const void *pte)
+static bool FNAME(fetch_guest_pte)(struct kvm_vcpu *vcpu,
+ struct kvm_mmu_page *sp, u64 *spte,
+ bool clear_unsync, pt_element_t gpte,
+ pfn_t (get_pfn)(struct kvm_vcpu *, u64 *,
+ pt_element_t, unsigned, bool *))
{
- pt_element_t gpte;
unsigned pte_access;
+ u64 nonpresent = shadow_trap_nonpresent_pte;
+ gfn_t gfn;
pfn_t pfn;
- u64 new_spte;
+ bool dirty, host_writeable;
- gpte = *(const pt_element_t *)pte;
- if (~gpte & (PT_PRESENT_MASK | PT_ACCESSED_MASK)) {
- if (!is_present_gpte(gpte)) {
- if (sp->unsync)
- new_spte = shadow_trap_nonpresent_pte;
- else
- new_spte = shadow_notrap_nonpresent_pte;
- __set_spte(spte, new_spte);
- }
- return;
+ if (!is_present_gpte(gpte) ||
+ is_rsvd_bits_set(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL)) {
+ if (!sp->unsync && !clear_unsync)
+ nonpresent = shadow_notrap_nonpresent_pte;
+ goto no_present;
}
- pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte);
+
+ if (!(gpte & PT_ACCESSED_MASK))
+ goto no_present;
+
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
+ gfn = gpte_to_gfn(gpte);
+ dirty = is_dirty_gpte(gpte);
+ pfn = get_pfn(vcpu, spte, gpte, pte_access, &host_writeable);
+
+ if (is_error_pfn(pfn))
+ goto no_present;
+
+ if (!host_writeable)
+ pte_access &= ~ACC_WRITE_MASK;
+
+ if (spte_to_pfn(*spte) == pfn)
+ set_spte(vcpu, spte, pte_access, 0, 0,
+ dirty, PT_PAGE_TABLE_LEVEL, gfn,
+ pfn, true, false, host_writeable);
+ else
+ mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0,
+ dirty, NULL, PT_PAGE_TABLE_LEVEL, gfn,
+ pfn, true, host_writeable);
+
+ return true;
+
+no_present:
+ drop_spte(vcpu->kvm, spte, nonpresent);
+ return false;
+}
+
+static pfn_t FNAME(get_update_pfn)(struct kvm_vcpu *vcpu, u64 *spte,
+ pt_element_t gpte, unsigned access,
+ bool *host_writeable)
+{
+ pfn_t pfn = bad_pfn;
+
if (gpte_to_gfn(gpte) != vcpu->arch.update_pte.gfn)
- return;
+ goto exit;
+
pfn = vcpu->arch.update_pte.pfn;
if (is_error_pfn(pfn))
- return;
- if (mmu_notifier_retry(vcpu, vcpu->arch.update_pte.mmu_seq))
- return;
- kvm_get_pfn(pfn);
+ goto exit;
+
+ if (mmu_notifier_retry(vcpu, vcpu->arch.update_pte.mmu_seq)) {
+ pfn = bad_pfn;
+ goto exit;
+ }
+
+
/*
- * we call mmu_set_spte() with host_writeable = true beacuse that
+ * we can set *host_writeable = true beacuse that
* vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1).
*/
- mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0,
- is_dirty_gpte(gpte), NULL, PT_PAGE_TABLE_LEVEL,
- gpte_to_gfn(gpte), pfn, true, true);
+ *host_writeable = true;
+ kvm_get_pfn(pfn);
+
+exit:
+ return pfn;
+}
+
+static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
+ u64 *spte, const void *pte)
+{
+ FNAME(fetch_guest_pte)(vcpu, sp, spte, false, *(pt_element_t *)pte,
+ FNAME(get_update_pfn));
}
static bool FNAME(gpte_changed)(struct kvm_vcpu *vcpu,
@@ -360,11 +408,26 @@ static bool FNAME(gpte_changed)(struct kvm_vcpu *vcpu,
return r || curr_pte != gw->ptes[level - 1];
}
+static pfn_t FNAME(get_prefetch_pfn)(struct kvm_vcpu *vcpu, u64 *spte,
+ pt_element_t gpte, unsigned access,
+ bool *host_writeable)
+{
+ pfn_t pfn;
+ bool dirty = is_dirty_gpte(gpte);
+
+ *host_writeable = true;
+ pfn = pte_prefetch_gfn_to_pfn(vcpu, gpte_to_gfn(gpte),
+ (access & ACC_WRITE_MASK) && dirty);
+ if (is_error_pfn(pfn))
+ kvm_release_pfn_clean(pfn);
+
+ return pfn;
+}
+
static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
u64 *sptep)
{
struct kvm_mmu_page *sp;
- struct kvm_mmu *mmu = &vcpu->arch.mmu;
pt_element_t *gptep = gw->prefetch_ptes;
u64 *spte;
int i;
@@ -382,10 +445,6 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
for (i = 0; i < PTE_PREFETCH_NUM; i++, spte++) {
pt_element_t gpte;
- unsigned pte_access;
- gfn_t gfn;
- pfn_t pfn;
- bool dirty;
if (spte == sptep)
continue;
@@ -394,30 +453,8 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
continue;
gpte = gptep[i];
-
- if (!is_present_gpte(gpte) ||
- is_rsvd_bits_set(mmu, gpte, PT_PAGE_TABLE_LEVEL)) {
- if (!sp->unsync)
- __set_spte(spte, shadow_notrap_nonpresent_pte);
- continue;
- }
-
- if (!(gpte & PT_ACCESSED_MASK))
- continue;
-
- pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
- gfn = gpte_to_gfn(gpte);
- dirty = is_dirty_gpte(gpte);
- pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn,
- (pte_access & ACC_WRITE_MASK) && dirty);
- if (is_error_pfn(pfn)) {
- kvm_release_pfn_clean(pfn);
- break;
- }
-
- mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0,
- dirty, NULL, PT_PAGE_TABLE_LEVEL, gfn,
- pfn, true, true);
+ FNAME(fetch_guest_pte)(vcpu, sp, spte, false, gpte,
+ FNAME(get_prefetch_pfn));
}
}
@@ -733,6 +770,20 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
}
}
+static pfn_t FNAME(get_sync_pfn)(struct kvm_vcpu *vcpu, u64 *spte,
+ pt_element_t gpte, unsigned access,
+ bool *host_writeable)
+{
+ struct kvm_mmu_page *sp = page_header(__pa(spte));
+
+ if (gpte_to_gfn(gpte) != sp->gfns[spte - sp->spt])
+ return bad_pfn;
+
+ *host_writeable = !!(*spte & SPTE_HOST_WRITEABLE);
+
+ return spte_to_pfn(*spte);
+}
+
/*
* 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
@@ -742,7 +793,6 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
bool clear_unsync)
{
int i, offset, nr_present;
- bool host_writeable;
gpa_t first_pte_gpa;
offset = nr_present = 0;
@@ -756,11 +806,8 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
first_pte_gpa = gfn_to_gpa(sp->gfn) + offset * sizeof(pt_element_t);
for (i = 0; i < PT64_ENT_PER_PAGE; i++) {
- unsigned pte_access;
pt_element_t gpte;
gpa_t pte_gpa;
- gfn_t gfn;
- bool gpte_invalid;
if (!is_shadow_present_pte(sp->spt[i]))
continue;
@@ -771,33 +818,9 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
sizeof(pt_element_t)))
return -EINVAL;
- gfn = gpte_to_gfn(gpte);
- gpte_invalid = is_present_gpte(gpte) ||
- is_rsvd_bits_set(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL);
- if (gpte_invalid || gfn != sp->gfns[i] ||
- !(gpte & PT_ACCESSED_MASK)) {
- u64 nonpresent;
-
- if (gpte_invalid || !clear_unsync)
- nonpresent = shadow_trap_nonpresent_pte;
- else
- nonpresent = shadow_notrap_nonpresent_pte;
- drop_spte(vcpu->kvm, &sp->spt[i], nonpresent);
- continue;
- }
-
- nr_present++;
- pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
- if (!(sp->spt[i] & SPTE_HOST_WRITEABLE)) {
- pte_access &= ~ACC_WRITE_MASK;
- host_writeable = 0;
- } else {
- host_writeable = 1;
- }
- set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
- is_dirty_gpte(gpte), PT_PAGE_TABLE_LEVEL, gfn,
- spte_to_pfn(sp->spt[i]), true, false,
- host_writeable);
+ if (FNAME(fetch_guest_pte)(vcpu, sp, &sp->spt[i], clear_unsync,
+ gpte, FNAME(get_sync_pfn)))
+ nr_present++;
}
return !nr_present;