Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bebb106a authored by Xiao Guangrong's avatar Xiao Guangrong Committed by Avi Kivity
Browse files

KVM: MMU: cache mmio info on page fault path



If the page fault is caused by mmio, we can cache the mmio info, later, we do
not need to walk guest page table and quickly know it is a mmio fault while we
emulate the mmio instruction

Signed-off-by: default avatarXiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent af7cc7d1
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -424,6 +424,11 @@ struct kvm_vcpu_arch {
	u64 mcg_ctl;
	u64 *mce_banks;

	/* Cache MMIO info */
	u64 mmio_gva;
	unsigned access;
	gfn_t mmio_gfn;

	/* used for guest single stepping over the given code position */
	unsigned long singlestep_rip;

+7 −14
Original line number Diff line number Diff line
@@ -217,11 +217,6 @@ void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask,
}
EXPORT_SYMBOL_GPL(kvm_mmu_set_mask_ptes);

static bool is_write_protection(struct kvm_vcpu *vcpu)
{
	return kvm_read_cr0_bits(vcpu, X86_CR0_WP);
}

static int is_cpuid_PSE36(void)
{
	return 1;
@@ -243,11 +238,6 @@ static int is_large_pte(u64 pte)
	return pte & PT_PAGE_SIZE_MASK;
}

static int is_writable_pte(unsigned long pte)
{
	return pte & PT_WRITABLE_MASK;
}

static int is_dirty_gpte(unsigned long pte)
{
	return pte & PT_DIRTY_MASK;
@@ -2247,15 +2237,17 @@ static void kvm_send_hwpoison_signal(unsigned long address, struct task_struct *
	send_sig_info(SIGBUS, &info, tsk);
}

static int kvm_handle_bad_page(struct kvm *kvm, gfn_t gfn, pfn_t pfn)
static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gva_t gva,
			       unsigned access, gfn_t gfn, pfn_t pfn)
{
	kvm_release_pfn_clean(pfn);
	if (is_hwpoison_pfn(pfn)) {
		kvm_send_hwpoison_signal(gfn_to_hva(kvm, gfn), current);
		kvm_send_hwpoison_signal(gfn_to_hva(vcpu->kvm, gfn), current);
		return 0;
	} else if (is_fault_pfn(pfn))
		return -EFAULT;

	vcpu_cache_mmio_info(vcpu, gva, gfn, access);
	return 1;
}

@@ -2337,7 +2329,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn,

	/* mmio */
	if (is_error_pfn(pfn))
		return kvm_handle_bad_page(vcpu->kvm, gfn, pfn);
		return kvm_handle_bad_page(vcpu, v, ACC_ALL, gfn, pfn);

	spin_lock(&vcpu->kvm->mmu_lock);
	if (mmu_notifier_retry(vcpu, mmu_seq))
@@ -2564,6 +2556,7 @@ static void mmu_sync_roots(struct kvm_vcpu *vcpu)
	if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
		return;

	vcpu_clear_mmio_info(vcpu, ~0ul);
	trace_kvm_mmu_audit(vcpu, AUDIT_PRE_SYNC);
	if (vcpu->arch.mmu.root_level == PT64_ROOT_LEVEL) {
		hpa_t root = vcpu->arch.mmu.root_hpa;
@@ -2710,7 +2703,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,

	/* mmio */
	if (is_error_pfn(pfn))
		return kvm_handle_bad_page(vcpu->kvm, gfn, pfn);
		return kvm_handle_bad_page(vcpu, 0, 0, gfn, pfn);
	spin_lock(&vcpu->kvm->mmu_lock);
	if (mmu_notifier_retry(vcpu, mmu_seq))
		goto out_unlock;
+23 −0
Original line number Diff line number Diff line
@@ -76,4 +76,27 @@ static inline int is_present_gpte(unsigned long pte)
	return pte & PT_PRESENT_MASK;
}

static inline int is_writable_pte(unsigned long pte)
{
	return pte & PT_WRITABLE_MASK;
}

static inline bool is_write_protection(struct kvm_vcpu *vcpu)
{
	return kvm_read_cr0_bits(vcpu, X86_CR0_WP);
}

static inline bool check_write_user_access(struct kvm_vcpu *vcpu,
					   bool write_fault, bool user_fault,
					   unsigned long pte)
{
	if (unlikely(write_fault && !is_writable_pte(pte)
	      && (user_fault || is_write_protection(vcpu))))
		return false;

	if (unlikely(user_fault && !(pte & PT_USER_MASK)))
		return false;

	return true;
}
#endif
+14 −7
Original line number Diff line number Diff line
@@ -208,11 +208,8 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
			goto error;
		}

		if (unlikely(write_fault && !is_writable_pte(pte)
			     && (user_fault || is_write_protection(vcpu))))
			eperm = true;

		if (unlikely(user_fault && !(pte & PT_USER_MASK)))
		if (!check_write_user_access(vcpu, write_fault, user_fault,
					  pte))
			eperm = true;

#if PTTYPE == 64
@@ -625,8 +622,16 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
		return 0;

	/* mmio */
	if (is_error_pfn(pfn))
		return kvm_handle_bad_page(vcpu->kvm, walker.gfn, pfn);
	if (is_error_pfn(pfn)) {
		unsigned access = walker.pte_access;
		bool dirty = is_dirty_gpte(walker.ptes[walker.level - 1]);

		if (!dirty)
			access &= ~ACC_WRITE_MASK;

		return kvm_handle_bad_page(vcpu, mmu_is_nested(vcpu) ? 0 :
					   addr, access, walker.gfn, pfn);
	}

	spin_lock(&vcpu->kvm->mmu_lock);
	if (mmu_notifier_retry(vcpu, mmu_seq))
@@ -666,6 +671,8 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
	u64 *sptep;
	int need_flush = 0;

	vcpu_clear_mmio_info(vcpu, gva);

	spin_lock(&vcpu->kvm->mmu_lock);

	for_each_shadow_entry(vcpu, gva, iterator) {
+11 −0
Original line number Diff line number Diff line
@@ -4016,6 +4016,14 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
{
	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;

	if (vcpu_match_mmio_gva(vcpu, gva) &&
		  check_write_user_access(vcpu, write, access,
		  vcpu->arch.access)) {
		*gpa = vcpu->arch.mmio_gfn << PAGE_SHIFT |
					(gva & (PAGE_SIZE - 1));
		return 1;
	}

	if (write)
		access |= PFERR_WRITE_MASK;

@@ -4028,6 +4036,9 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
	if ((*gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
		return 1;

	if (vcpu_match_mmio_gpa(vcpu, *gpa))
		return 1;

	return 0;
}

Loading