[RFC PATCH v3 1/6] lib: sbi: Preserve hypervisor and VS state across domain switches

Samuel Holland samuel.holland at sifive.com
Thu Nov 5 07:02:02 PST 2026


On 2026-11-04 10:43 PM, Theodorus Wensan Februanto wrote:
> Save outgoing VS state before modifying S/H interrupt configuration,
> then restore incoming VS state after the H state exchange.
> 
> Preserve core H-extension CSRs, optional Smaia hypervisor interrupt
> state, and Smstateen state-enable registers.
> 
> Include hvien, hvictl, hviprio1, hviprio2, and the RV32 VS interrupt
> high-half CSRs.
> 
> Link: https://github.com/riscv-software-src/opensbi/issues/433
> Signed-off-by: Theodorus Wensan Februanto <theodoruswensanfebruanto025 at gmail.com>
> ---
>  lib/sbi/sbi_domain_context.c | 112 +++++++++++++++++++++++++++++++++++
>  1 file changed, 112 insertions(+)
> 
> diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
> index 987ac7fa..c34d4cbb 100644
> --- a/lib/sbi/sbi_domain_context.c
> +++ b/lib/sbi/sbi_domain_context.c
> @@ -21,6 +21,42 @@
>  #include <sbi/sbi_vector.h>
>  #include <sbi/sbi_fp.h>
>  
> +struct hart_context_hext {
> +	unsigned long hstatus;
> +	unsigned long hedeleg;
> +	unsigned long hideleg;
> +	unsigned long hie;
> +	unsigned long hgeie;
> +	u64 henvcfg;
> +	unsigned long htval;
> +	unsigned long htinst;
> +	unsigned long hvip;
> +	unsigned long hgatp;
> +	/** Optional hypervisor state */
> +	u64 hvien;
> +	unsigned long hvictl;
> +	u64 hviprio1;
> +	u64 hviprio2;
> +	u64 hstateen[4];
> +#if __riscv_xlen == 32
> +	unsigned long hidelegh;
> +	unsigned long hviph;
> +	unsigned long vsieh;
> +	unsigned long vsiph;
> +#endif
> +	unsigned long hcounteren;
> +	u64 htimedelta;
> +	unsigned long vsstatus;
> +	unsigned long vsie;
> +	unsigned long vstvec;
> +	unsigned long vsscratch;
> +	unsigned long vsepc;
> +	unsigned long vscause;
> +	unsigned long vstval;
> +	unsigned long vsip;
> +	unsigned long vsatp;
> +};
> +
>  /** Context representation for a hart within a domain */
>  struct hart_context {
>  	/** Trap-related states such as GPRs, mepc, and mstatus */
> @@ -44,6 +80,8 @@ struct hart_context {
>  	unsigned long sip;
>  	/** Supervisor address translation and protection register */
>  	unsigned long satp;
> +
> +	struct hart_context_hext hext;
>  	/** Counter-enable register */
>  	unsigned long scounteren;
>  	/** Supervisor environment configuration register */
> @@ -131,6 +169,25 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
>  	sbi_hartmask_set_hartindex(hartindex, &target_dom->assigned_harts);
>  	spin_unlock(&target_dom->assigned_harts_lock);
>  
> +	/* Save outgoing VS state before modifying S/H interrupt CSRs. */
> +	if (misa_extension('H')) {
> +		ctx->hext.vsstatus = csr_read(CSR_VSSTATUS);
> +		ctx->hext.vsie = csr_read(CSR_VSIE);
> +		ctx->hext.vstvec = csr_read(CSR_VSTVEC);
> +		ctx->hext.vsscratch = csr_read(CSR_VSSCRATCH);
> +		ctx->hext.vsepc = csr_read(CSR_VSEPC);
> +		ctx->hext.vscause = csr_read(CSR_VSCAUSE);
> +		ctx->hext.vstval = csr_read(CSR_VSTVAL);
> +		ctx->hext.vsip = csr_read(CSR_VSIP);
> +		ctx->hext.vsatp = csr_read(CSR_VSATP);
> +#if __riscv_xlen == 32
> +		if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA)) {
> +			ctx->hext.vsieh = csr_read(CSR_VSIEH);
> +			ctx->hext.vsiph = csr_read(CSR_VSIPH);
> +		}
> +#endif
> +	}
> +
>  	/* Save current CSR context and restore target domain's CSR context */
>  	ctx->sstatus	= csr_swap(CSR_SSTATUS, dom_ctx->sstatus);
>  	ctx->sie	= csr_swap(CSR_SIE, dom_ctx->sie);
> @@ -141,6 +198,61 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
>  	ctx->stval	= csr_swap(CSR_STVAL, dom_ctx->stval);
>  	ctx->sip	= csr_swap(CSR_SIP, dom_ctx->sip);
>  	ctx->satp	= csr_swap(CSR_SATP, dom_ctx->satp);
> +
> +	if (misa_extension('H')) {
> +		ctx->hext.hstatus = csr_swap(CSR_HSTATUS, dom_ctx->hext.hstatus);
> +		ctx->hext.hedeleg = csr_swap(CSR_HEDELEG, dom_ctx->hext.hedeleg);
> +		ctx->hext.hideleg = csr_swap(CSR_HIDELEG, dom_ctx->hext.hideleg);
> +		ctx->hext.hie = csr_swap(CSR_HIE, dom_ctx->hext.hie);
> +		ctx->hext.hgeie = csr_swap(CSR_HGEIE, dom_ctx->hext.hgeie);
> +		ctx->hext.henvcfg = csr_swap64(CSR_HENVCFG, dom_ctx->hext.henvcfg);

OpenSBI supports hardware with H but not Sm1p12 (e.g. SiFive P550), so this CSR
needs an extension check.

Additionally, since the bits this CSR control if certain bits in senvcfg are
writable or read-only zero, the save/restore of senvcfg needs to be split.

> +		ctx->hext.htval = csr_swap(CSR_HTVAL, dom_ctx->hext.htval);
> +		ctx->hext.htinst = csr_swap(CSR_HTINST, dom_ctx->hext.htinst);
> +		ctx->hext.hvip = csr_swap(CSR_HVIP, dom_ctx->hext.hvip);
> +		ctx->hext.hgatp = csr_swap(CSR_HGATP, dom_ctx->hext.hgatp);
> +		ctx->hext.hcounteren = csr_swap(CSR_HCOUNTEREN, dom_ctx->hext.hcounteren);
> +		ctx->hext.htimedelta = csr_swap64(CSR_HTIMEDELTA, dom_ctx->hext.htimedelta);

Similarly, OpenSBI supports hardware with H but not Zicntr, in which case the
htimedelta CSR is not implemented, and the delta is implemented via
sbi_timer_get/set_delta().

> +#if __riscv_xlen == 32
> +		if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA)) {
> +			ctx->hext.hidelegh = csr_swap(CSR_HIDELEGH,
> +						dom_ctx->hext.hidelegh);
> +			ctx->hext.hviph = csr_swap(CSR_HVIPH, dom_ctx->hext.hviph);
> +		}
> +#endif
> +		if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA)) {

Is it necessary to check for Smaia twice here?

Regards,
Samuel

> +			ctx->hext.hvien = csr_swap64(CSR_HVIEN, dom_ctx->hext.hvien);
> +			ctx->hext.hvictl = csr_swap(CSR_HVICTL, dom_ctx->hext.hvictl);
> +			ctx->hext.hviprio1 = csr_swap64(CSR_HVIPRIO1, dom_ctx->hext.hviprio1);
> +			ctx->hext.hviprio2 = csr_swap64(CSR_HVIPRIO2, dom_ctx->hext.hviprio2);
> +		}
> +
> +		if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMSTATEEN)) {
> +			ctx->hext.hstateen[0] = csr_swap64(CSR_HSTATEEN0,
> +						dom_ctx->hext.hstateen[0]);
> +			ctx->hext.hstateen[1] = csr_swap64(CSR_HSTATEEN1,
> +						dom_ctx->hext.hstateen[1]);
> +			ctx->hext.hstateen[2] = csr_swap64(CSR_HSTATEEN2,
> +						dom_ctx->hext.hstateen[2]);
> +			ctx->hext.hstateen[3] = csr_swap64(CSR_HSTATEEN3,
> +						dom_ctx->hext.hstateen[3]);
> +		}
> +		/* Restore incoming VS state after H interrupt configuration. */
> +		csr_write(CSR_VSSTATUS, dom_ctx->hext.vsstatus);
> +		csr_write(CSR_VSIE, dom_ctx->hext.vsie);
> +		csr_write(CSR_VSTVEC, dom_ctx->hext.vstvec);
> +		csr_write(CSR_VSSCRATCH, dom_ctx->hext.vsscratch);
> +		csr_write(CSR_VSEPC, dom_ctx->hext.vsepc);
> +		csr_write(CSR_VSCAUSE, dom_ctx->hext.vscause);
> +		csr_write(CSR_VSTVAL, dom_ctx->hext.vstval);
> +		csr_write(CSR_VSIP, dom_ctx->hext.vsip);
> +		csr_write(CSR_VSATP, dom_ctx->hext.vsatp);
> +#if __riscv_xlen == 32
> +		if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA)) {
> +			csr_write(CSR_VSIEH, dom_ctx->hext.vsieh);
> +			csr_write(CSR_VSIPH, dom_ctx->hext.vsiph);
> +		}
> +#endif
> +	}
>  	if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
>  		ctx->scounteren = csr_swap(CSR_SCOUNTEREN, dom_ctx->scounteren);
>  	if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12)




More information about the opensbi mailing list