[PATCH v4 RESEND 1/5] KVM: riscv: Avoid overwriting G-stage tables with huge leaves

Anup Patel anup at brainfault.org
Sat Oct 3 14:30:50 PDT 2026


On Sat, Aug 15, 2026 at 4:01 PM Jinyu Tang <jinyu.tang at linux.dev> wrote:
>
> RISC-V KVM can overwrite an existing G-stage table entry when
> installing a huge leaf mapping. If the target huge range already has a
> lower-level page table, kvm_riscv_gstage_set_pte() can replace the
> non-leaf entry with a leaf PTE and disconnect the lower-level page
> table.
>
> Reject replacing a valid table entry with a leaf PTE. If huge-page
> installation hits such a conflict, fall back to a 4K mapping for the
> original faulting GPA in the MMU fault path, where the original GPA and
> HFN are still available.
>
> Suggested-by: Sashiko <sashiko-bot at kernel.org>
> Fixes: 9d05c1fee837 ("RISC-V: KVM: Implement stage2 page table programming")
> Signed-off-by: Jinyu Tang <jinyu.tang at linux.dev>
> ---
>  arch/riscv/kvm/gstage.c |  6 ++++++
>  arch/riscv/kvm/mmu.c    | 24 +++++++++++++++++++++++-
>  2 files changed, 29 insertions(+), 1 deletion(-)
>
> diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
> index e5002cb9cbef..54d45addf18f 100644
> --- a/arch/riscv/kvm/gstage.c
> +++ b/arch/riscv/kvm/gstage.c
> @@ -174,6 +174,12 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,
>
>         if (pte_val(*ptep) != pte_val(map->pte)) {
>                 bool was_invalid = !pte_val(*ptep);
> +
> +               /* Avoid replacing an existing lower-level table with a leaf mapping. */
> +               if (!gstage_pte_leaf(ptep) && !was_invalid &&
> +                   gstage_pte_leaf(&map->pte))
> +                       return -EEXIST;
> +
>                 set_pte(ptep, map->pte);
>                 if (gstage_pte_leaf(ptep) &&
>                     !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)))
> diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
> index 6035b5ec9503..bfd6168ebe30 100644
> --- a/arch/riscv/kvm/mmu.c
> +++ b/arch/riscv/kvm/mmu.c
> @@ -625,10 +625,11 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
>                       struct kvm_gstage_mapping *out_map)
>  {
>         int ret;
> -       kvm_pfn_t hfn;
> +       kvm_pfn_t fault_hfn, hfn;
>         bool is_hugetlb;
>         bool writable;
>         unsigned int vma_pageshift;
> +       gpa_t fault_gpa = gpa;
>         gfn_t gfn = gpa >> PAGE_SHIFT;
>         struct vm_area_struct *vma;
>         struct kvm *kvm = vcpu->kvm;
> @@ -709,6 +710,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
>         }
>         if (is_error_noslot_pfn(hfn))
>                 return -EFAULT;
> +       fault_hfn = hfn + ((fault_gpa >> PAGE_SHIFT) - gfn);
>
>         /*
>          * If logging is active then we allow writable pages only
> @@ -734,9 +736,29 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
>                 mark_page_dirty_in_slot(kvm, memslot, gfn);
>                 ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
>                                                 vma_pagesize, false, true, out_map);
> +               if (ret == -EEXIST) {
> +                       /*
> +                        * Retry at 4K granularity for the original faulting GPA
> +                        * when a huge leaf cannot replace an existing table.
> +                        */
> +                       ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
> +                                                       fault_hfn << PAGE_SHIFT,
> +                                                       PAGE_SIZE, false, true,
> +                                                       out_map);
> +               }

If you were attempting to map 1G hugepage then you should first try
2M and not 4K. This patch needs more work.

>         } else {
>                 ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
>                                                 vma_pagesize, true, true, out_map);
> +               if (ret == -EEXIST) {
> +                       /*
> +                        * Retry at 4K granularity for the original faulting GPA
> +                        * when a huge leaf cannot replace an existing table.
> +                        */
> +                       ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
> +                                                       fault_hfn << PAGE_SHIFT,
> +                                                       PAGE_SIZE, true, true,
> +                                                       out_map);
> +               }
>         }
>
>         if (ret)
> --
> 2.43.0
>

Regards,
Anup



More information about the linux-riscv mailing list