lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260120080013.2153519-16-anup.patel@oss.qualcomm.com>
Date: Tue, 20 Jan 2026 13:30:01 +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 15/27] RISC-V: KVM: Extend trap redirection for nested virtualization

The L0/host hypervisor must always redirect traps to the L1/guest
hypervisor so extend KVM RISC-V to perform the necessary nested
world-switch when redirecting traps.

Signed-off-by: Anup Patel <anup.patel@....qualcomm.com>
---
 arch/riscv/include/asm/kvm_host.h        |   3 +
 arch/riscv/include/asm/kvm_vcpu_nested.h |  12 ++
 arch/riscv/kvm/vcpu_exit.c               |  28 +++-
 arch/riscv/kvm/vcpu_nested.c             | 162 +++++++++++++++++++++++
 4 files changed, 201 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index 3b58953eb4eb..c510564a09a2 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -289,6 +289,9 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
 					 bool read_insn,
 					 unsigned long guest_addr,
 					 struct kvm_cpu_trap *trap);
+void kvm_riscv_vcpu_trap_smode_redirect(struct kvm_vcpu *vcpu,
+					struct kvm_cpu_trap *trap,
+					bool prev_priv);
 void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu,
 				  struct kvm_cpu_trap *trap);
 int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
diff --git a/arch/riscv/include/asm/kvm_vcpu_nested.h b/arch/riscv/include/asm/kvm_vcpu_nested.h
index 4234c6e81bb6..6bfb67702610 100644
--- a/arch/riscv/include/asm/kvm_vcpu_nested.h
+++ b/arch/riscv/include/asm/kvm_vcpu_nested.h
@@ -75,6 +75,18 @@ 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);
 
+enum kvm_vcpu_nested_set_virt_event {
+	NESTED_SET_VIRT_EVENT_TRAP = 0,
+	NESTED_SET_VIRT_EVENT_SRET
+};
+
+void kvm_riscv_vcpu_nested_set_virt(struct kvm_vcpu *vcpu,
+				    enum kvm_vcpu_nested_set_virt_event event,
+				    bool virt, bool spvp, bool gva);
+void kvm_riscv_vcpu_nested_trap_redirect(struct kvm_vcpu *vcpu,
+					 struct kvm_cpu_trap *trap,
+					 bool prev_priv);
+
 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);
diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c
index 4f63548e582f..aeec4c4eee06 100644
--- a/arch/riscv/kvm/vcpu_exit.c
+++ b/arch/riscv/kvm/vcpu_exit.c
@@ -149,19 +149,21 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
 }
 
 /**
- * kvm_riscv_vcpu_trap_redirect -- Redirect trap to Guest
+ * kvm_riscv_vcpu_trap_smode_redirect -- Redirect S-mode trap to Guest
  *
  * @vcpu: The VCPU pointer
  * @trap: Trap details
+ * @prev_priv: Previous privilege mode (true: S-mode, false: U-mode)
  */
-void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu,
-				  struct kvm_cpu_trap *trap)
+void kvm_riscv_vcpu_trap_smode_redirect(struct kvm_vcpu *vcpu,
+					struct kvm_cpu_trap *trap,
+					bool prev_priv)
 {
 	unsigned long vsstatus = ncsr_read(CSR_VSSTATUS);
 
 	/* Change Guest SSTATUS.SPP bit */
 	vsstatus &= ~SR_SPP;
-	if (vcpu->arch.guest_context.sstatus & SR_SPP)
+	if (prev_priv)
 		vsstatus |= SR_SPP;
 
 	/* Change Guest SSTATUS.SPIE bit */
@@ -187,6 +189,24 @@ void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu,
 	vcpu->arch.guest_context.sstatus |= SR_SPP;
 }
 
+/**
+ * kvm_riscv_vcpu_trap_redirect -- Redirect HS-mode trap to Guest
+ *
+ * @vcpu: The VCPU pointer
+ * @trap: Trap details
+ */
+void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu,
+				  struct kvm_cpu_trap *trap)
+{
+	bool prev_priv = (vcpu->arch.guest_context.sstatus & SR_SPP) ? true : false;
+
+	/* Update Guest nested state */
+	kvm_riscv_vcpu_nested_trap_redirect(vcpu, trap, prev_priv);
+
+	/* Update Guest supervisor state */
+	kvm_riscv_vcpu_trap_smode_redirect(vcpu, trap, prev_priv);
+}
+
 static inline int vcpu_redirect(struct kvm_vcpu *vcpu, struct kvm_cpu_trap *trap)
 {
 	int ret = -EFAULT;
diff --git a/arch/riscv/kvm/vcpu_nested.c b/arch/riscv/kvm/vcpu_nested.c
index 3c30d35b3b39..214206fc28bb 100644
--- a/arch/riscv/kvm/vcpu_nested.c
+++ b/arch/riscv/kvm/vcpu_nested.c
@@ -3,13 +3,175 @@
  * Copyright (c) 2026 Qualcomm Technologies, Inc.
  */
 
+#include <linux/smp.h>
 #include <linux/kvm_host.h>
+#include <asm/kvm_nacl.h>
+#include <asm/kvm_mmu.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_set_virt(struct kvm_vcpu *vcpu,
+				    enum kvm_vcpu_nested_set_virt_event event,
+				    bool virt, bool spvp, bool gva)
+{
+	struct kvm_vcpu_nested *ns = &vcpu->arch.nested;
+	struct kvm_vcpu_nested_csr *nsc = &ns->csr;
+	unsigned long tmp, sr_fs_vs_mask = 0;
+	int cpu;
+
+	/* If H-extension is not available for VCPU then do nothing */
+	if (!riscv_isa_extension_available(vcpu->arch.isa, h))
+		return;
+
+	/* Grab the CPU to ensure we remain on same CPU */
+	cpu = get_cpu();
+
+	/* Skip hardware CSR update if no change in virt state */
+	if (virt == ns->virt)
+		goto skip_csr_update;
+
+	/* Update config CSRs (aka hedeleg, hideleg, henvcfg, and hstateeX) */
+	kvm_riscv_vcpu_config_load(vcpu, virt);
+
+	/* Update time delta */
+	kvm_riscv_vcpu_update_timedelta(vcpu, virt);
+
+	/* Update G-stage page table */
+	kvm_riscv_mmu_update_hgatp(vcpu, virt);
+
+	/* Swap hardware vs<xyz> CSRs except vsie and vsstatus */
+	nsc->vstvec = ncsr_swap(CSR_VSTVEC, nsc->vstvec);
+	nsc->vsscratch = ncsr_swap(CSR_VSSCRATCH, nsc->vsscratch);
+	nsc->vsepc = ncsr_swap(CSR_VSEPC, nsc->vsepc);
+	nsc->vscause = ncsr_swap(CSR_VSCAUSE, nsc->vscause);
+	nsc->vstval = ncsr_swap(CSR_VSTVAL, nsc->vstval);
+	nsc->vsatp = ncsr_swap(CSR_VSATP, nsc->vsatp);
+
+	/* Update vsstatus CSR */
+	if (riscv_isa_extension_available(vcpu->arch.isa, f) ||
+	    riscv_isa_extension_available(vcpu->arch.isa, d))
+		sr_fs_vs_mask |= SR_FS;
+	if (riscv_isa_extension_available(vcpu->arch.isa, v))
+		sr_fs_vs_mask |= SR_VS;
+	if (virt) {
+		/*
+		 * Update vsstatus in following manner:
+		 * 1) Swap hardware vsstatus (i.e. virtual-HS mode sstatus) with
+		 *    vsstatus in nested virtualization context (i.e. virtual-VS
+		 *    mode sstatus)
+		 * 2) Swap host sstatus.[FS|VS] (i.e. HS mode sstatus.[FS|VS])
+		 *    with the vsstatus.[FS|VS] saved in nested virtualization
+		 *    context (i.e. virtual-HS mode sstatus.[FS|VS])
+		 */
+		nsc->vsstatus = ncsr_swap(CSR_VSSTATUS, nsc->vsstatus);
+		tmp = vcpu->arch.guest_context.sstatus & sr_fs_vs_mask;
+		vcpu->arch.guest_context.sstatus &= ~sr_fs_vs_mask;
+		vcpu->arch.guest_context.sstatus |= (nsc->vsstatus & sr_fs_vs_mask);
+		nsc->vsstatus &= ~sr_fs_vs_mask;
+		nsc->vsstatus |= tmp;
+	} else {
+		/*
+		 * Update vsstatus in following manner:
+		 * 1) Swap host sstatus.[FS|VS] (i.e. virtual-HS mode sstatus.[FS|VS])
+		 *    with vsstatus.[FS|VS] saved in the nested virtualization
+		 *    context (i.e. HS mode sstatus.[FS|VS])
+		 * 2) Swap hardware vsstatus (i.e. virtual-VS mode sstatus) with
+		 *    vsstatus in nested virtualization context (i.e. virtual-HS
+		 *    mode sstatus)
+		 */
+		tmp = vcpu->arch.guest_context.sstatus & sr_fs_vs_mask;
+		vcpu->arch.guest_context.sstatus &= ~sr_fs_vs_mask;
+		vcpu->arch.guest_context.sstatus |= (nsc->vsstatus & sr_fs_vs_mask);
+		nsc->vsstatus &= ~sr_fs_vs_mask;
+		nsc->vsstatus |= tmp;
+		nsc->vsstatus = ncsr_swap(CSR_VSSTATUS, nsc->vsstatus);
+	}
+
+skip_csr_update:
+	if (event != NESTED_SET_VIRT_EVENT_SRET) {
+		/* Update guest hstatus.SPV bit */
+		nsc->hstatus &= ~HSTATUS_SPV;
+		nsc->hstatus |= (ns->virt) ? HSTATUS_SPV : 0;
+
+		/* Update guest hstatus.SPVP bit */
+		if (ns->virt) {
+			nsc->hstatus &= ~HSTATUS_SPVP;
+			if (spvp)
+				nsc->hstatus |= HSTATUS_SPVP;
+		}
+
+		/* Update guest hstatus.GVA bit */
+		if (event == NESTED_SET_VIRT_EVENT_TRAP) {
+			nsc->hstatus &= ~HSTATUS_GVA;
+			nsc->hstatus |= (gva) ? HSTATUS_GVA : 0;
+		}
+	}
+
+	/* Update host SRET trapping */
+	vcpu->arch.guest_context.hstatus &= ~HSTATUS_VTSR;
+	if (virt) {
+		if (nsc->hstatus & HSTATUS_VTSR)
+			vcpu->arch.guest_context.hstatus |= HSTATUS_VTSR;
+	} else {
+		if (nsc->hstatus & HSTATUS_SPV)
+			vcpu->arch.guest_context.hstatus |= HSTATUS_VTSR;
+	}
+
+	/* Update host VM trapping */
+	vcpu->arch.guest_context.hstatus &= ~HSTATUS_VTVM;
+	if (virt && (nsc->hstatus & HSTATUS_VTVM))
+		vcpu->arch.guest_context.hstatus |= HSTATUS_VTVM;
+
+	/* Update virt flag */
+	ns->virt = virt;
+
+	/* Release CPU */
+	put_cpu();
+}
+
+void kvm_riscv_vcpu_nested_trap_redirect(struct kvm_vcpu *vcpu,
+					 struct kvm_cpu_trap *trap,
+					 bool prev_priv)
+{
+	bool gva;
+
+	/* Do nothing if H-extension is not available for VCPU */
+	if (!riscv_isa_extension_available(vcpu->arch.isa, h))
+		return;
+
+	/* Determine GVA bit state */
+	gva = false;
+	switch (trap->scause) {
+	case EXC_INST_MISALIGNED:
+	case EXC_INST_ACCESS:
+	case EXC_LOAD_MISALIGNED:
+	case EXC_LOAD_ACCESS:
+	case EXC_STORE_MISALIGNED:
+	case EXC_STORE_ACCESS:
+	case EXC_INST_PAGE_FAULT:
+	case EXC_LOAD_PAGE_FAULT:
+	case EXC_STORE_PAGE_FAULT:
+	case EXC_INST_GUEST_PAGE_FAULT:
+	case EXC_LOAD_GUEST_PAGE_FAULT:
+	case EXC_STORE_GUEST_PAGE_FAULT:
+		gva = true;
+		break;
+	default:
+		break;
+	}
+
+	/* Update Guest HTVAL and HTINST */
+	vcpu->arch.nested.csr.htval = trap->htval;
+	vcpu->arch.nested.csr.htinst = trap->htinst;
+
+	/* Turn-off nested virtualization for virtual-HS mode */
+	kvm_riscv_vcpu_nested_set_virt(vcpu, NESTED_SET_VIRT_EVENT_TRAP,
+				       false, prev_priv, gva);
+}
+
 void kvm_riscv_vcpu_nested_reset(struct kvm_vcpu *vcpu)
 {
 	struct kvm_vcpu_nested *ns = &vcpu->arch.nested;
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ