[PATCH 1/2] lib: sbi: sse: fix KVM context corruption

Zhanpeng Zhang zhangzhanpeng.jasper at bytedance.com
Thu Apr 30 03:23:12 PDT 2026


SSE injection builds a synthetic S-mode context to enter the registered
S-mode handler. If the interrupted context belongs to KVM guest
execution, OpenSBI must not let that synthetic context overwrite the
virtualization state needed to resume the interrupted context.

KVM Context Corruption happens because the old complete path rebuilt
hstatus SPV/SPVP from interrupted flags and derived MPV from
handler-visible hstatus. However, this is insufficient on
virtualization-enabled systems: hstatus carries more H-mode state, and
MPV must come from the state captured before OpenSBI prepares the
synthetic handler-entry context. Reconstructing these values from
handler-visible state can corrupt the KVM resume context.

Save the complete hstatus value and the interrupted MPV state in
OpenSBI's private SSE resume state before constructing the handler
context. The state is stored in sbi_sse_event because it should persist
from injection to completion, but it must not be part of the ABI-visible
SSE attributes that the S-mode handler can update.

This is the OpenSBI side of the KVM context corruption fix. The related
Linux side should preserve the kernel stvec in do_sse() to help protect
the virtualization context.

Link: https://lore.kernel.org/r/9290f53d-3545-4299-9781-c1c558f71158@rivosinc.com
Fixes: c8cdf01d8f3a ("lib: sbi: Add support for Supervisor Software Events extension")
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper at bytedance.com>
---
 lib/sbi/sbi_sse.c | 53 ++++++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 21 deletions(-)

diff --git a/lib/sbi/sbi_sse.c b/lib/sbi/sbi_sse.c
index 818afb87..0544449d 100644
--- a/lib/sbi/sbi_sse.c
+++ b/lib/sbi/sbi_sse.c
@@ -70,6 +70,19 @@ struct sse_ipi_inject_data {
 	uint32_t event_id;
 };
 
+/*
+ * OpenSBI-private state used to resume the interrupted context after the SSE
+ * event handler completes. Keep this separate from SSE attributes: S-mode can
+ * update attributes before completion, while these fields preserve
+ * M-mode-owned state.
+ */
+struct sse_resume_state {
+	/* Complete hstatus value, used to restore H-mode virtualization state. */
+	unsigned long hstatus;
+	/* MPV bit from mstatus/mstatusH, used to restore virtualization state. */
+	bool prev_virt;
+};
+
 struct sbi_sse_event_attrs {
 	unsigned long status;
 	unsigned long prio;
@@ -100,6 +113,7 @@ assert_field_offset(interrupted.a7, SBI_SSE_ATTR_INTERRUPTED_A7);
 
 struct sbi_sse_event {
 	struct sbi_sse_event_attrs attrs;
+	struct sse_resume_state resume;
 	uint32_t event_id;
 	u32 hartindex;
 	struct sse_event_info *info;
@@ -545,6 +559,7 @@ static void sse_event_inject(struct sbi_sse_event *e,
 			     struct sbi_trap_regs *regs)
 {
 	struct sse_interrupted_state *i_ctx = &e->attrs.interrupted;
+	struct sse_resume_state *r_ctx = &e->resume;
 
 	sse_event_set_state(e, SBI_SSE_STATE_RUNNING);
 
@@ -552,9 +567,10 @@ static void sse_event_inject(struct sbi_sse_event *e,
 
 	i_ctx->a6 = regs->a6;
 	i_ctx->a7 = regs->a7;
-	i_ctx->flags = sse_interrupted_flags(regs->mstatus);
 	i_ctx->sepc = csr_read(CSR_SEPC);
 
+	r_ctx->prev_virt = sbi_regs_from_virt(regs);
+
 	regs->mstatus &= ~(MSTATUS_SPP | SSTATUS_SPIE);
 	if (regs->mstatus & MSTATUS_MPP)
 		regs->mstatus |= MSTATUS_SPP;
@@ -563,22 +579,24 @@ static void sse_event_inject(struct sbi_sse_event *e,
 
 	if (misa_extension('H')) {
 		unsigned long hstatus = csr_read(CSR_HSTATUS);
+		unsigned long prev_mode = (regs->mstatus & MSTATUS_MPP) >>
+					  MSTATUS_MPP_SHIFT;
 
-#if __riscv_xlen == 64
-		if (regs->mstatus & MSTATUS_MPV)
-#elif __riscv_xlen == 32
-		if (regs->mstatusH & MSTATUSH_MPV)
-#else
-#error "Unexpected __riscv_xlen"
-#endif
+		r_ctx->hstatus = hstatus;
+
+		if (r_ctx->prev_virt)
 			hstatus |= HSTATUS_SPV;
+		else
+			hstatus &= ~HSTATUS_SPV;
 
 		hstatus &= ~HSTATUS_SPVP;
-		if (hstatus & HSTATUS_SPV && regs->mstatus & SSTATUS_SPP)
-				hstatus |= HSTATUS_SPVP;
+		if ((hstatus & HSTATUS_SPV) && prev_mode == PRV_S)
+			hstatus |= HSTATUS_SPVP;
 
 		csr_write(CSR_HSTATUS, hstatus);
 	}
+
+	i_ctx->flags = sse_interrupted_flags(regs->mstatus);
 	csr_write(CSR_SEPC, regs->mepc);
 
 	/* Setup entry context */
@@ -608,6 +626,7 @@ static void sse_event_resume(struct sbi_sse_event *e,
 			     struct sbi_trap_regs *regs)
 {
 	struct sse_interrupted_state *i_ctx = &e->attrs.interrupted;
+	struct sse_resume_state *r_ctx = &e->resume;
 
 	regs->mepc = csr_read(CSR_SEPC);
 
@@ -616,26 +635,18 @@ static void sse_event_resume(struct sbi_sse_event *e,
 		regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
 
 	if (misa_extension('H')) {
-		unsigned long hstatus = csr_read(CSR_HSTATUS);
 #if __riscv_xlen == 64
 		regs->mstatus &= ~MSTATUS_MPV;
-		if (hstatus & HSTATUS_SPV)
+		if (r_ctx->prev_virt)
 			regs->mstatus |= MSTATUS_MPV;
 #elif __riscv_xlen == 32
 		regs->mstatusH &= ~MSTATUSH_MPV;
-		if (hstatus & HSTATUS_SPV)
+		if (r_ctx->prev_virt)
 			regs->mstatusH |= MSTATUSH_MPV;
 #else
 #error "Unexpected __riscv_xlen"
 #endif
-		hstatus &= ~(HSTATUS_SPV | HSTATUS_SPVP);
-		if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV)
-			hstatus |= HSTATUS_SPV;
-
-		if (i_ctx->flags & SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP)
-			hstatus |= HSTATUS_SPVP;
-
-		csr_write(CSR_HSTATUS, hstatus);
+		csr_write(CSR_HSTATUS, r_ctx->hstatus);
 	}
 
 	regs->mstatus &= ~MSTATUS_SIE;
-- 
2.50.1 (Apple Git-155)




More information about the opensbi mailing list