The generic KVM_PRE_FAULT_MEMORY ioctl lets userspace populate KVM page
tables before running a vCPU over a GPA range. x86 already supports the
ioctl, but RISC-V does not expose the capability and has no arch hook.

Add the RISC-V arch hook and reuse the existing G-stage fault mapping
path with a read access. Report progress using the G-stage mapping
returned by the map path, so the ioctl can advance by the actual leaf
size that covers the requested GPA. Retry until a mapping is installed
or a signal, VM-dead request, or real error is observed.

Signed-off-by: Jinyu Tang <[email protected]>
---
 arch/riscv/kvm/Kconfig  |  1 +
 arch/riscv/kvm/gstage.c |  3 +++
 arch/riscv/kvm/mmu.c    | 45 ++++++++++++++++++++++++++++++++++++++++-
 arch/riscv/kvm/vm.c     |  1 +
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..8ac209e8ac87 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,6 +28,7 @@ config KVM
        select KVM_COMMON
        select KVM_GENERIC_DIRTYLOG_READ_PROTECT
        select KVM_GENERIC_HARDWARE_ENABLING
+       select KVM_GENERIC_PRE_FAULT_MEMORY
        select KVM_MMIO
        select VIRT_XFER_TO_GUEST_WORK
        select SCHED_INFO
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 54d45addf18f..dff315dfd24e 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -286,6 +286,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
                                                    out_map->level, true);
                } else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), 
page_size) == hpa) {
                        kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, 
gpa, ptep, prot);
+                       out_map->addr = ALIGN_DOWN(gpa, page_size);
+                       out_map->level = ptep_level;
+                       out_map->pte = ptep_get(ptep);
                        return 0;
                }
        }
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 2fabcd409991..f6ca86da53eb 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -717,7 +717,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct 
kvm_memory_slot *memslot,
        if (hfn == KVM_PFN_ERR_HWPOISON) {
                send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva,
                                vma_pageshift, current);
-               return 0;
+               return -EFAULT;
        }
        if (is_error_noslot_pfn(hfn))
                return -EFAULT;
@@ -782,6 +782,49 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct 
kvm_memory_slot *memslot,
        return ret;
 }
 
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+                                   struct kvm_pre_fault_memory *range)
+{
+       struct kvm_gstage_mapping out_map = { 0 };
+       struct kvm_memory_slot *memslot;
+       unsigned long map_size;
+       unsigned long hva;
+       gpa_t end;
+       gfn_t gfn;
+       int ret;
+
+       gfn = gpa_to_gfn(range->gpa);
+       memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+       if (!memslot)
+               return -ENOENT;
+
+       hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+       if (kvm_is_error_hva(hva))
+               return -ENOENT;
+
+       for (;;) {
+               if (signal_pending(current))
+                       return -EINTR;
+
+               if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+                       return -EIO;
+
+               cond_resched();
+               ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, 
&out_map);
+               if (ret)
+                       return ret;
+
+               if (!pte_val(out_map.pte))
+                       continue;
+
+               map_size = PAGE_SIZE << (out_map.level * 
kvm_riscv_gstage_index_bits);
+               end = out_map.addr + map_size;
+               break;
+       }
+
+       return min_t(u64, range->size, end - range->gpa);
+}
+
 int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
 {
        struct page *pgd_page;
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb76..58500a19b33b 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        case KVM_CAP_MP_STATE:
        case KVM_CAP_IMMEDIATE_EXIT:
        case KVM_CAP_SET_GUEST_DEBUG:
+       case KVM_CAP_PRE_FAULT_MEMORY:
                r = 1;
                break;
        case KVM_CAP_NR_VCPUS:
-- 
2.43.0


Reply via email to