[PATCH v4 1/5] KVM: riscv: Avoid overwriting G-stage tables with huge leaves
Jinyu Tang
jinyu.tang at linux.dev
Sat Aug 15 03:00:15 PDT 2026
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);
+ }
} 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
More information about the linux-riscv
mailing list