]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
KVM: arm64: nv: Handle VNCR_EL2-triggered faults backed by guest_memfd
authorFuad Tabba <tabba@google.com>
Tue, 29 Jul 2025 22:54:50 +0000 (15:54 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 27 Aug 2025 08:36:50 +0000 (04:36 -0400)
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: Marc Zyngier <maz@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20250729225455.670324-20-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/arm64/kvm/nested.c

index 153b3e11b115d2f8338859ab19803ef4957d5751..27ebcae352999a6ffed0584c729949828b5dfe6b 100644 (file)
@@ -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,10 +1217,25 @@ 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);
-       if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
+       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))
                        return -EAGAIN;
@@ -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: