[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260120080013.2153519-11-anup.patel@oss.qualcomm.com>
Date: Tue, 20 Jan 2026 13:29:56 +0530
From: Anup Patel <anup.patel@....qualcomm.com>
To: Paolo Bonzini <pbonzini@...hat.com>, Atish Patra <atish.patra@...ux.dev>
Cc: Palmer Dabbelt <palmer@...belt.com>, Paul Walmsley <pjw@...nel.org>,
Alexandre Ghiti <alex@...ti.fr>, Shuah Khan <shuah@...nel.org>,
Anup Patel <anup@...infault.org>,
Andrew Jones <andrew.jones@....qualcomm.com>,
kvm-riscv@...ts.infradead.org, kvm@...r.kernel.org,
linux-riscv@...ts.infradead.org, linux-kernel@...r.kernel.org,
linux-kselftest@...r.kernel.org,
Anup Patel <anup.patel@....qualcomm.com>
Subject: [PATCH 10/27] RISC-V: KVM: Initial skeletal nested virtualization support
Add initial skeletal nested virtualization support which is disabled
by default and needs to be explicitly enabled using module parameter.
Subsequent patches will further improve and complete the nested
virtualization support.
Signed-off-by: Anup Patel <anup.patel@....qualcomm.com>
---
arch/riscv/include/asm/kvm_host.h | 5 ++
arch/riscv/include/asm/kvm_vcpu_nested.h | 83 ++++++++++++++++++++++++
arch/riscv/kvm/Makefile | 2 +
arch/riscv/kvm/isa.c | 2 +-
arch/riscv/kvm/main.c | 5 ++
arch/riscv/kvm/vcpu.c | 20 +++++-
arch/riscv/kvm/vcpu_exit.c | 22 ++++++-
arch/riscv/kvm/vcpu_nested.c | 48 ++++++++++++++
arch/riscv/kvm/vcpu_nested_swtlb.c | 70 ++++++++++++++++++++
9 files changed, 253 insertions(+), 4 deletions(-)
create mode 100644 arch/riscv/include/asm/kvm_vcpu_nested.h
create mode 100644 arch/riscv/kvm/vcpu_nested.c
create mode 100644 arch/riscv/kvm/vcpu_nested_swtlb.c
diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index 11c3566318ae..3b58953eb4eb 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -25,6 +25,7 @@
#include <asm/kvm_vcpu_sbi_fwft.h>
#include <asm/kvm_vcpu_timer.h>
#include <asm/kvm_vcpu_pmu.h>
+#include <asm/kvm_vcpu_nested.h>
#define KVM_MAX_VCPUS 1024
@@ -45,6 +46,7 @@
#define KVM_REQ_HFENCE \
KVM_ARCH_REQ_FLAGS(5, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(6)
+#define KVM_REQ_NESTED_SWTLB KVM_ARCH_REQ(7)
#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE
@@ -203,6 +205,9 @@ struct kvm_vcpu_arch {
/* CPU reset state of Guest VCPU */
struct kvm_vcpu_reset_state reset_state;
+ /* CPU nested virtualization context of Guest VCPU */
+ struct kvm_vcpu_nested nested;
+
/*
* VCPU interrupts
*
diff --git a/arch/riscv/include/asm/kvm_vcpu_nested.h b/arch/riscv/include/asm/kvm_vcpu_nested.h
new file mode 100644
index 000000000000..4234c6e81bb6
--- /dev/null
+++ b/arch/riscv/include/asm/kvm_vcpu_nested.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+
+#ifndef __RISCV_VCPU_NESTED_H__
+#define __RISCV_VCPU_NESTED_H__
+
+#include <linux/jump_label.h>
+#include <linux/kvm_types.h>
+#include <asm/kvm_mmu.h>
+
+DECLARE_STATIC_KEY_FALSE(kvm_riscv_nested_available);
+#define kvm_riscv_nested_available() \
+ static_branch_unlikely(&kvm_riscv_nested_available)
+
+struct kvm_vcpu_nested_swtlb {
+ /* Software TLB request */
+ struct {
+ bool pending;
+ struct kvm_gstage_mapping guest;
+ struct kvm_gstage_mapping host;
+ } request;
+
+ /* Shadow G-stage page table for guest VS/VU-mode */
+ pgd_t *shadow_pgd;
+ phys_addr_t shadow_pgd_phys;
+};
+
+struct kvm_vcpu_nested_csr {
+ unsigned long hstatus;
+ unsigned long hedeleg;
+ unsigned long hideleg;
+ unsigned long hvip;
+ unsigned long hcounteren;
+ unsigned long htimedelta;
+ unsigned long htimedeltah;
+ unsigned long htval;
+ unsigned long htinst;
+ unsigned long henvcfg;
+ unsigned long henvcfgh;
+ unsigned long hgatp;
+ unsigned long vsstatus;
+ unsigned long vsie;
+ unsigned long vstvec;
+ unsigned long vsscratch;
+ unsigned long vsepc;
+ unsigned long vscause;
+ unsigned long vstval;
+ unsigned long vsatp;
+};
+
+struct kvm_vcpu_nested {
+ /* Nested virt state */
+ bool virt;
+
+ /* Nested software TLB request */
+ struct kvm_vcpu_nested_swtlb swtlb;
+
+ /* Nested CSR state */
+ struct kvm_vcpu_nested_csr csr;
+};
+
+#define kvm_riscv_vcpu_nested_virt(__vcpu) ((__vcpu)->arch.nested.virt)
+
+int kvm_riscv_vcpu_nested_swtlb_xlate(struct kvm_vcpu *vcpu,
+ const struct kvm_cpu_trap *trap,
+ struct kvm_gstage_mapping *out_map,
+ struct kvm_cpu_trap *out_trap);
+void kvm_riscv_vcpu_nested_swtlb_process(struct kvm_vcpu *vcpu);
+void kvm_riscv_vcpu_nested_swtlb_request(struct kvm_vcpu *vcpu,
+ const struct kvm_gstage_mapping *guest_map,
+ const struct kvm_gstage_mapping *host_map);
+void kvm_riscv_vcpu_nested_swtlb_reset(struct kvm_vcpu *vcpu);
+int kvm_riscv_vcpu_nested_swtlb_init(struct kvm_vcpu *vcpu);
+void kvm_riscv_vcpu_nested_swtlb_deinit(struct kvm_vcpu *vcpu);
+
+void kvm_riscv_vcpu_nested_reset(struct kvm_vcpu *vcpu);
+int kvm_riscv_vcpu_nested_init(struct kvm_vcpu *vcpu);
+void kvm_riscv_vcpu_nested_deinit(struct kvm_vcpu *vcpu);
+void kvm_riscv_nested_init(void);
+
+#endif
diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile
index 296c2ba05089..a8806b69205f 100644
--- a/arch/riscv/kvm/Makefile
+++ b/arch/riscv/kvm/Makefile
@@ -25,6 +25,8 @@ kvm-y += vcpu_config.o
kvm-y += vcpu_exit.o
kvm-y += vcpu_fp.o
kvm-y += vcpu_insn.o
+kvm-y += vcpu_nested.o
+kvm-y += vcpu_nested_swtlb.o
kvm-y += vcpu_onereg.o
kvm-$(CONFIG_RISCV_PMU_SBI) += vcpu_pmu.o
kvm-y += vcpu_sbi.o
diff --git a/arch/riscv/kvm/isa.c b/arch/riscv/kvm/isa.c
index e860f6d79bb0..1566d01fc52e 100644
--- a/arch/riscv/kvm/isa.c
+++ b/arch/riscv/kvm/isa.c
@@ -145,7 +145,7 @@ bool kvm_riscv_isa_enable_allowed(unsigned long ext)
{
switch (ext) {
case KVM_RISCV_ISA_EXT_H:
- return false;
+ return kvm_riscv_nested_available();
case KVM_RISCV_ISA_EXT_SSCOFPMF:
/* Sscofpmf depends on interrupt filtering defined in ssaia */
return !kvm_riscv_isa_check_host(SSAIA);
diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c
index 588a84783dff..5b4bf972d242 100644
--- a/arch/riscv/kvm/main.c
+++ b/arch/riscv/kvm/main.c
@@ -131,6 +131,8 @@ static int __init riscv_kvm_init(void)
return rc;
}
+ kvm_riscv_nested_init();
+
kvm_info("hypervisor extension available\n");
if (kvm_riscv_nacl_available()) {
@@ -172,6 +174,9 @@ static int __init riscv_kvm_init(void)
kvm_info("AIA available with %d guest external interrupts\n",
kvm_riscv_aia_nr_hgei);
+ if (kvm_riscv_nested_available())
+ kvm_info("nested virtualization available\n");
+
kvm_riscv_setup_vendor_features();
kvm_register_perf_callbacks(NULL);
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index 93c731da67f6..859c8e71df65 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -94,6 +94,8 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
kvm_riscv_vcpu_context_reset(vcpu, kvm_sbi_reset);
+ kvm_riscv_vcpu_nested_reset(vcpu);
+
kvm_riscv_vcpu_fp_reset(vcpu);
kvm_riscv_vcpu_vector_reset(vcpu);
@@ -152,10 +154,16 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
spin_lock_init(&vcpu->arch.reset_state.lock);
- rc = kvm_riscv_vcpu_alloc_vector_context(vcpu);
+ rc = kvm_riscv_vcpu_nested_init(vcpu);
if (rc)
return rc;
+ rc = kvm_riscv_vcpu_alloc_vector_context(vcpu);
+ if (rc) {
+ kvm_riscv_vcpu_nested_deinit(vcpu);
+ return rc;
+ }
+
/* Setup VCPU timer */
kvm_riscv_vcpu_timer_init(vcpu);
@@ -205,6 +213,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
/* Free vector context space for host and guest kernel */
kvm_riscv_vcpu_free_vector_context(vcpu);
+
+ /* Cleanup VCPU nested state */
+ kvm_riscv_vcpu_nested_deinit(vcpu);
}
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
@@ -683,6 +694,13 @@ static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)
if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu))
kvm_riscv_vcpu_record_steal_time(vcpu);
+ /*
+ * Process nested software TLB request after handling
+ * various HFENCE requests.
+ */
+ if (kvm_check_request(KVM_REQ_NESTED_SWTLB, vcpu))
+ kvm_riscv_vcpu_nested_swtlb_process(vcpu);
+
if (kvm_dirty_ring_check_request(vcpu))
return 0;
}
diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c
index 0bb0c51e3c89..4f63548e582f 100644
--- a/arch/riscv/kvm/vcpu_exit.c
+++ b/arch/riscv/kvm/vcpu_exit.c
@@ -15,14 +15,29 @@
static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_cpu_trap *trap)
{
- struct kvm_gstage_mapping host_map;
+ struct kvm_gstage_mapping guest_map, host_map;
struct kvm_memory_slot *memslot;
unsigned long hva, fault_addr;
+ struct kvm_cpu_trap out_trap;
bool writable;
gfn_t gfn;
int ret;
- fault_addr = (trap->htval << 2) | (trap->stval & 0x3);
+ if (kvm_riscv_vcpu_nested_virt(vcpu)) {
+ memset(&out_trap, 0, sizeof(out_trap));
+ ret = kvm_riscv_vcpu_nested_swtlb_xlate(vcpu, trap, &guest_map, &out_trap);
+ if (ret <= 0)
+ return ret;
+ fault_addr = __page_val_to_pfn(pte_val(guest_map.pte)) << PAGE_SHIFT;
+
+ if (out_trap.scause) {
+ kvm_riscv_vcpu_trap_redirect(vcpu, &out_trap);
+ return 1;
+ }
+ } else {
+ fault_addr = (trap->htval << 2) | (trap->stval & 0x3);
+ }
+
gfn = fault_addr >> PAGE_SHIFT;
memslot = gfn_to_memslot(vcpu->kvm, gfn);
hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
@@ -49,6 +64,9 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
if (ret < 0)
return ret;
+ if (kvm_riscv_vcpu_nested_virt(vcpu) && !pte_none(host_map.pte))
+ kvm_riscv_vcpu_nested_swtlb_request(vcpu, &guest_map, &host_map);
+
return 1;
}
diff --git a/arch/riscv/kvm/vcpu_nested.c b/arch/riscv/kvm/vcpu_nested.c
new file mode 100644
index 000000000000..3c30d35b3b39
--- /dev/null
+++ b/arch/riscv/kvm/vcpu_nested.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/kvm_host.h>
+
+DEFINE_STATIC_KEY_FALSE(kvm_riscv_nested_available);
+
+static bool __read_mostly enable_nested_virt;
+module_param(enable_nested_virt, bool, 0644);
+
+void kvm_riscv_vcpu_nested_reset(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_nested *ns = &vcpu->arch.nested;
+ struct kvm_vcpu_nested_csr *ncsr = &vcpu->arch.nested.csr;
+
+ ns->virt = false;
+ kvm_riscv_vcpu_nested_swtlb_reset(vcpu);
+ memset(ncsr, 0, sizeof(*ncsr));
+}
+
+int kvm_riscv_vcpu_nested_init(struct kvm_vcpu *vcpu)
+{
+ return kvm_riscv_vcpu_nested_swtlb_init(vcpu);
+}
+
+void kvm_riscv_vcpu_nested_deinit(struct kvm_vcpu *vcpu)
+{
+ kvm_riscv_vcpu_nested_swtlb_deinit(vcpu);
+}
+
+void kvm_riscv_nested_init(void)
+{
+ /*
+ * Nested virtualization uses hvictl CSR hence only
+ * available when AIA is available.
+ */
+ if (!kvm_riscv_aia_available())
+ return;
+
+ /* Check state of module parameter */
+ if (!enable_nested_virt)
+ return;
+
+ /* Enable KVM nested virtualization support */
+ static_branch_enable(&kvm_riscv_nested_available);
+}
diff --git a/arch/riscv/kvm/vcpu_nested_swtlb.c b/arch/riscv/kvm/vcpu_nested_swtlb.c
new file mode 100644
index 000000000000..1d9faf50a61f
--- /dev/null
+++ b/arch/riscv/kvm/vcpu_nested_swtlb.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/kvm_host.h>
+
+int kvm_riscv_vcpu_nested_swtlb_xlate(struct kvm_vcpu *vcpu,
+ const struct kvm_cpu_trap *trap,
+ struct kvm_gstage_mapping *out_map,
+ struct kvm_cpu_trap *out_trap)
+{
+ /* TODO: */
+ return 0;
+}
+
+void kvm_riscv_vcpu_nested_swtlb_process(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_nested_swtlb *nst = &vcpu->arch.nested.swtlb;
+
+ WARN_ON(!nst->request.pending);
+
+ /* TODO: */
+
+ nst->request.pending = false;
+}
+
+void kvm_riscv_vcpu_nested_swtlb_request(struct kvm_vcpu *vcpu,
+ const struct kvm_gstage_mapping *guest_map,
+ const struct kvm_gstage_mapping *host_map)
+{
+ struct kvm_vcpu_nested_swtlb *nst = &vcpu->arch.nested.swtlb;
+
+ WARN_ON(nst->request.pending);
+
+ nst->request.pending = true;
+ memcpy(&nst->request.guest, guest_map, sizeof(*guest_map));
+ memcpy(&nst->request.host, host_map, sizeof(*host_map));
+
+ kvm_make_request(KVM_REQ_NESTED_SWTLB, vcpu);
+}
+
+void kvm_riscv_vcpu_nested_swtlb_reset(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_nested_swtlb *nst = &vcpu->arch.nested.swtlb;
+
+ memset(nst, 0, sizeof(*nst));
+}
+
+int kvm_riscv_vcpu_nested_swtlb_init(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_nested_swtlb *nst = &vcpu->arch.nested.swtlb;
+ struct page *pgd_page;
+
+ pgd_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(kvm_riscv_gstage_pgd_size));
+ if (!pgd_page)
+ return -ENOMEM;
+ nst->shadow_pgd = page_to_virt(pgd_page);
+ nst->shadow_pgd_phys = page_to_phys(pgd_page);
+
+ return 0;
+}
+
+void kvm_riscv_vcpu_nested_swtlb_deinit(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_nested_swtlb *nst = &vcpu->arch.nested.swtlb;
+
+ free_pages((unsigned long)nst->shadow_pgd, get_order(kvm_riscv_gstage_pgd_size));
+}
--
2.43.0
Powered by blists - more mailing lists