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.

Unlike x86, RISC-V's G-stage fault path does not return a detailed
result such as RET_PF_FIXED or RET_PF_RETRY. Treat a successful call
that does not return a visible G-stage mapping as no progress, instead
of reporting that a page was prefaulted.

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

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 e5002cb9cbef..6bd8b8fd6ceb 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -280,6 +280,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 6035b5ec9503..c96e21740c91 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -748,6 +748,39 @@ 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;
+
+       ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, 
&out_map);
+       if (ret)
+               return ret;
+
+       if (!pte_val(out_map.pte))
+               return -EAGAIN;
+
+       map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
+       end = ALIGN_DOWN(range->gpa, map_size) + map_size;
+
+       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