Re: [PATCH v2 3/3] lib: sbi: domain FP/Vector context support for context switch

Samuel Holland
2026-03-26T14:41:24Z

On 2026-03-26 6:55 AM, dave.patel@riscstar.com wrote:
> From: Dave Patel <dave.patel@riscstar.com>
>
> This patch adds proper support for per-domain floating-point (FP) and
> vector (V) contexts in the domain context switch logic. Each domain
> now maintains its own FP and vector state, which is saved and restored
> during domain switches.
>
> Changes include:
>
> - Added `fp_ctx` and `vec_ctx` members to `struct sbi_domain`.
> - Introduced `sbi_fp_domain_init/exit()` and `sbi_vector_domain_init/exit()`
> to allocate and free per-domain FP and vector context.
> - Modified `sbi_domain_register()` to initialize FP/Vector context per domain.
> - Updated `switch_to_next_domain_context()` to save/restore FP and vector
> contexts safely:
> - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off.
> - Restores original FS/VS bits after context switch.
> - Adds NULL checks to handle domains without FP or Vector extensions.
> - Updated domain context deinit to free FP and vector contexts per domain.
> - Added runtime checks for FP and vector extensions where needed.
> - Corrected handling of MSTATUS FS/VS bits to avoid unsafe full-bit writes.
>
> This improves support for multi-domain systems with FP and Vector
> extensions, and prevents corruption of FP/Vector state during domain
> switches.
>
> Signed-off-by: Dave Patel <dave.patel@riscstar.com>
> ---
> include/sbi/sbi_domain.h | 4 ++++
> include/sbi/sbi_fp.h | 7 ++++++
> include/sbi/sbi_vector.h | 5 +++++
> lib/sbi/sbi_domain.c | 22 +++++++++++++++++++
> lib/sbi/sbi_domain_context.c | 42 ++++++++++++++++++++++++++++++++++++
> lib/sbi/sbi_fp.c | 31 ++++++++++++++++++++++++++
> lib/sbi/sbi_vector.c | 30 ++++++++++++++++++++++++++
> 7 files changed, 141 insertions(+)
>
> diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h
> index 882b62c2..e68ec46d 100644
> --- a/include/sbi/sbi_domain.h
> +++ b/include/sbi/sbi_domain.h
> @@ -217,6 +217,10 @@ struct sbi_domain {
> bool fw_region_inited;
> /** per-domain wired-IRQ courier state */
> void *virq_priv;

Patch submissions should be based on top of the upstream master branch, unless
noted in the cover letter. Please rebase.

> + /** per-domain float context state */
> + void *fp_ctx;
> + /** per-domain vector context state */
> + void *vec_ctx;

There is no need for indirection through a pointer and the overhead of a
separate allocation. Please embed the fp/vector context inside this struct. Then
you do not need separate init/exit functions.

> };
>
> /** The root domain instance */
> diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h
> index 5794b66f..2de9bf04 100644
> --- a/include/sbi/sbi_fp.h
> +++ b/include/sbi/sbi_fp.h
> @@ -9,6 +9,9 @@
> #define __SBI_FP_H__
>
> #include <sbi/riscv_encoding.h>
> +#include <sbi/sbi_domain.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_console.h>

This looks like leftover debugging code.

>
> #if defined(__riscv_f) || defined(__riscv_d)
>
> @@ -25,6 +28,8 @@ struct sbi_fp_context {
>
> void sbi_fp_save(struct sbi_fp_context *dst);
> void sbi_fp_restore(const struct sbi_fp_context *src);
> +int sbi_fp_domain_init(struct sbi_domain *dom);
> +void sbi_fp_domain_exit(struct sbi_domain *dom);
>
> #else /* No FP (e.g., Zve32x) */
>
> @@ -32,6 +37,8 @@ struct sbi_fp_context { };
>
> static inline void sbi_fp_save(struct sbi_fp_context *dst) { }
> static inline void sbi_fp_restore(const struct sbi_fp_context *src) { }
> +static inline int sbi_fp_domain_init(struct sbi_domain *dom) { return SBI_OK;}
> +static inline void sbi_fp_domain_exit(struct sbi_domain *dom) {}
>
> #endif //defined(__riscv_f) || defined(__riscv_d)
> #endif //__SBI_VECTOR_H__
> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h
> index ae151406..c8295f05 100644
> --- a/include/sbi/sbi_vector.h
> +++ b/include/sbi/sbi_vector.h
> @@ -10,6 +10,9 @@
> #define __SBI_VECTOR_H__
>
> #include <sbi/sbi_types.h>
> +#include <sbi/sbi_domain.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_console.h>

ditto

>
> #ifdef CONFIG_SBI_MAX_VLENB
> #define SBI_MAX_VLENB CONFIG_SBI_MAX_VLENB
> @@ -30,6 +33,8 @@ struct sbi_vector_context {
> struct sbi_vector_context *sbi_current_vector_context(void);
> void sbi_vector_save(struct sbi_vector_context *dst);
> void sbi_vector_restore(const struct sbi_vector_context *src);
> +int sbi_vector_domain_init(struct sbi_domain *dom);
> +void sbi_vector_domain_exit(struct sbi_domain *dom);
>
> #endif //__SBI_VECTOR_H__
>
> diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c
> index 498a1d56..cdc416b4 100644
> --- a/lib/sbi/sbi_domain.c
> +++ b/lib/sbi/sbi_domain.c
> @@ -19,6 +19,8 @@
> #include <sbi/sbi_scratch.h>
> #include <sbi/sbi_string.h>
> #include <sbi/sbi_virq.h>
> +#include <sbi/sbi_vector.h>
> +#include <sbi/sbi_fp.h>
>
> SBI_LIST_HEAD(domain_list);
>
> @@ -703,6 +705,26 @@ int sbi_domain_register(struct sbi_domain *dom,
> return rc;
> }
>
> + /* Init per-domain floating context */
> + rc = sbi_fp_domain_init(dom);
> + if (rc) {
> + sbi_printf("%s: fp init failed for %s (error %d)\n",
> + __func__, dom->name, rc);
> + sbi_list_del(&dom->node);
> + return rc;
> + }
> +
> +#ifdef __riscv_v
> + /* Init per-domain vector context */
> + rc = sbi_vector_domain_init(dom);
> + if (rc) {
> + sbi_printf("%s: vec init failed for %s (error %d)\n",
> + __func__, dom->name, rc);
> + sbi_list_del(&dom->node);
> + return rc;
> + }
> +#endif
> +
> return 0;
> }
>
> diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
> index 158f4990..fb843a91 100644
> --- a/lib/sbi/sbi_domain_context.c
> +++ b/lib/sbi/sbi_domain_context.c
> @@ -18,6 +18,9 @@
> #include <sbi/sbi_domain_context.h>
> #include <sbi/sbi_platform.h>
> #include <sbi/sbi_trap.h>
> +#include <sbi/sbi_vector.h>
> +#include <sbi/sbi_fp.h>
> +
>
> /** Context representation for a hart within a domain */
> struct hart_context {
> @@ -143,6 +146,37 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
> if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID))
> ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg);
>
> + /* Read current mstatus */
> + unsigned long mstatus = csr_read(CSR_MSTATUS);
> + unsigned long new_mstatus = mstatus;
> +
> + /* Ensure FS is enabled (not Off) */
> + if ((mstatus & MSTATUS_FS) == 0)
> + new_mstatus |= MSTATUS_FS;
> +
> +#ifdef __riscv_v
> + /* Ensure VS is enabled (not Off) */
> + if ((mstatus & MSTATUS_VS) == 0)
> + new_mstatus |= MSTATUS_VS;
> +#endif
> +
> + /* Update mstatus only if needed */
> + if (new_mstatus != mstatus)
> + csr_write(CSR_MSTATUS, new_mstatus);

FS and VS are WARL. This above block can be just:

csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS);

You don't need to restore mstatus because it will be loaded from the new
domain's trap_ctx anyway (and the old domain's mstatus was already saved).

Regards,
Samuel

> +
> + /* Save current domain context and restore target domain's F and V context */
> + sbi_fp_save(current_dom->fp_ctx);
> + sbi_fp_restore(target_dom->fp_ctx);
> +#ifdef __riscv_v
> + sbi_vector_save(current_dom->vec_ctx);
> + sbi_vector_restore(target_dom->vec_ctx);
> +#endif
> +
> + /* Restore original mstatus if we modified it */
> + if (new_mstatus != mstatus) {
> + csr_write(CSR_MSTATUS, mstatus);
> + }
> +
> /* Save current trap state and restore target domain's trap state */
> trap_ctx = sbi_trap_get_context(scratch);
> sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx));
> @@ -286,5 +320,13 @@ int sbi_domain_context_init(void)
>
> void sbi_domain_context_deinit(void)
> {
> + struct sbi_domain *dom;
> + sbi_domain_for_each(dom) {
> +#ifdef __riscv_v
> + sbi_vector_domain_exit(dom);
> +#endif
> + sbi_fp_domain_exit(dom);
> + }
> +
> sbi_domain_unregister_data(&dcpriv);
> }
> diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c
> index b6651577..ee0782d1 100644
> --- a/lib/sbi/sbi_fp.c
> +++ b/lib/sbi/sbi_fp.c
> @@ -7,6 +7,7 @@
> */
>
> #include <sbi/riscv_asm.h>
> +#include <sbi/sbi_heap.h>
> #include <sbi/riscv_encoding.h>
> #include <sbi/sbi_fp.h>
>
> @@ -185,4 +186,34 @@ void sbi_fp_restore(const struct sbi_fp_context *src)
>
> csr_write(CSR_FCSR, src->fcsr);
> }
> +
> +int sbi_fp_domain_init(struct sbi_domain *dom)
> +{
> + if (!dom)
> + return SBI_EINVAL;
> +
> + if (dom->fp_ctx)
> + return SBI_OK;
> +
> + sbi_printf("[FPU] Init FP Context \n");
> +
> + struct sbi_fp_context *fp;
> + fp = sbi_zalloc(sizeof(*fp));
> + if (!fp)
> + return SBI_ENOMEM;
> +
> + dom->fp_ctx = fp;
> +
> + return SBI_OK;
> +}
> +
> +void sbi_fp_domain_exit(struct sbi_domain *dom)
> +{
> + if (!dom || !dom->fp_ctx)
> + return;
> +
> + sbi_free(dom->fp_ctx);
> + dom->fp_ctx = NULL;
> +}
> +
> #endif // FP present
> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c
> index e14b658c..2dfb9fbf 100644
> --- a/lib/sbi/sbi_vector.c
> +++ b/lib/sbi/sbi_vector.c
> @@ -10,6 +10,7 @@
> #include <sbi/riscv_encoding.h>
> #include <sbi/riscv_asm.h>
> #include <sbi/sbi_vector.h>
> +#include <sbi/sbi_heap.h>
>
> static inline unsigned long vector_vlenb(void)
> {
> @@ -144,3 +145,32 @@ void sbi_vector_restore(const struct sbi_vector_context *src)
>
> #undef RESTORE_VREG
> }
> +
> +int sbi_vector_domain_init(struct sbi_domain *dom)
> +{
> + if (!dom)
> + return SBI_EINVAL;
> +
> + if (dom->vec_ctx)
> + return SBI_OK;
> +
> + sbi_printf("[Vector] Init Vector Context \n");
> +
> + struct sbi_vector_context *v;
> + v = sbi_zalloc(sizeof(*v));
> + if (!v)
> + return SBI_ENOMEM;
> +
> + dom->vec_ctx = v;
> +
> + return SBI_OK;
> +}
> +
> +void sbi_vector_domain_exit(struct sbi_domain *dom)
> +{
> + if (!dom || !dom->vec_ctx)
> + return;
> +
> + sbi_free(dom->vec_ctx);
> + dom->vec_ctx = NULL;
> +}
> --
> 2.43.0
>