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

Anup Patel anup at brainfault.org
Thu Apr 30 21:57:22 PDT 2026


On Thu, Apr 30, 2026 at 3:53 PM Zhanpeng Zhang
<zhangzhanpeng.jasper at bytedance.com> wrote:
>
> 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);

This is plain wrong and does not align with the SBI SSE spec.
The SSE interrupted flags must be saved before hstatus
and sstatus bits are modified.

If you want to save restore whole hstatus CSR then right place
to do that is do_sse() function at <linux>/arch/riscv/kernel/sbi_sse.c

The responsibility of SBI implementation (OpenSBI) is save/restore
only minimal possible state upon SSE entry/exit. In addition, the
SBI implementation must also allow supervisor software to update
any save/restore state using SSE attributes.

>         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)
>

Regards,
Anup



More information about the opensbi mailing list