Commit f4e74030 authored by Fuad Tabba's avatar Fuad Tabba Committed by Paolo Bonzini
Browse files

KVM: arm64: nv: Handle VNCR_EL2-triggered faults backed by guest_memfd



Handle faults for memslots backed by guest_memfd in arm64 nested
virtualization triggered by VNCR_EL2.

* Introduce is_gmem output parameter to kvm_translate_vncr(), indicating
  whether the faulted memory slot is backed by guest_memfd.

* Dispatch faults backed by guest_memfd to kvm_gmem_get_pfn().

* Update kvm_handle_vncr_abort() to handle potential guest_memfd errors.
  Some of the guest_memfd errors need to be handled by userspace instead
  of attempting to (implicitly) retry by returning to the guest.

Suggested-by: default avatarMarc Zyngier <maz@kernel.org>
Reviewed-by: default avatarMarc Zyngier <maz@kernel.org>
Signed-off-by: default avatarFuad Tabba <tabba@google.com>
Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Message-ID: <20250729225455.670324-20-seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent a7b57e09
Loading
Loading
Loading
Loading
+35 −6
Original line number Diff line number Diff line
@@ -1172,8 +1172,9 @@ static u64 read_vncr_el2(struct kvm_vcpu *vcpu)
	return (u64)sign_extend64(__vcpu_sys_reg(vcpu, VNCR_EL2), 48);
}

static int kvm_translate_vncr(struct kvm_vcpu *vcpu)
static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
{
	struct kvm_memory_slot *memslot;
	bool write_fault, writable;
	unsigned long mmu_seq;
	struct vncr_tlb *vt;
@@ -1216,9 +1217,24 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu)
	smp_rmb();

	gfn = vt->wr.pa >> PAGE_SHIFT;
	pfn = kvm_faultin_pfn(vcpu, gfn, write_fault, &writable, &page);
	memslot = gfn_to_memslot(vcpu->kvm, gfn);
	if (!memslot)
		return -EFAULT;

	*is_gmem = kvm_slot_has_gmem(memslot);
	if (!*is_gmem) {
		pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0,
					&writable, &page);
		if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
			return -EFAULT;
	} else {
		ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
		if (ret) {
			kvm_prepare_memory_fault_exit(vcpu, vt->wr.pa, PAGE_SIZE,
					      write_fault, false, false);
			return ret;
		}
	}

	scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
		if (mmu_invalidate_retry(vcpu->kvm, mmu_seq))
@@ -1292,23 +1308,36 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
	if (esr_fsc_is_permission_fault(esr)) {
		inject_vncr_perm(vcpu);
	} else if (esr_fsc_is_translation_fault(esr)) {
		bool valid;
		bool valid, is_gmem = false;
		int ret;

		scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
			valid = kvm_vncr_tlb_lookup(vcpu);

		if (!valid)
			ret = kvm_translate_vncr(vcpu);
			ret = kvm_translate_vncr(vcpu, &is_gmem);
		else
			ret = -EPERM;

		switch (ret) {
		case -EAGAIN:
		case -ENOMEM:
			/* Let's try again... */
			break;
		case -ENOMEM:
			/*
			 * For guest_memfd, this indicates that it failed to
			 * create a folio to back the memory. Inform userspace.
			 */
			if (is_gmem)
				return 0;
			/* Otherwise, let's try again... */
			break;
		case -EFAULT:
		case -EIO:
		case -EHWPOISON:
			if (is_gmem)
				return 0;
			fallthrough;
		case -EINVAL:
		case -ENOENT:
		case -EACCES: