[RFC PATCH v4 4/5] lib: sbi: Preserve supervisor CTR records across domain switches
Theodorus Wensan Februanto
theodoruswensanfebruanto025 at gmail.com
Sat Nov 7 15:44:20 PST 2026
Save supervisor CTR control, depth, status, and logical record
entries when Ssctr and Sscsrind are available. This experimental
implementation freezes recording before saving and restores
status before logical records.
IMSIC and other indirect CSR state remain outside this change.
Link: https://github.com/riscv-software-src/opensbi/issues/433
Signed-off-by: Theodorus Wensan Februanto <theodoruswensanfebruanto025 at gmail.com>
---
include/sbi/sbi_hart.h | 2 +
lib/sbi/sbi_domain_context.c | 147 ++++++++++++++++++++++++++++++++++-
lib/sbi/sbi_hart.c | 11 +++
3 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
index a020cf03..6f2ef04b 100644
--- a/include/sbi/sbi_hart.h
+++ b/include/sbi/sbi_hart.h
@@ -127,6 +127,8 @@ struct sbi_hart_features {
unsigned int pmp_log2gran;
unsigned int mhpm_mask;
unsigned int mhpm_bits;
+ unsigned long ctr_depth;
+ unsigned int ctr_num_entries;
unsigned long expected_trap;
};
diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c
index b5989516..ac67d60c 100644
--- a/lib/sbi/sbi_domain_context.c
+++ b/lib/sbi/sbi_domain_context.c
@@ -22,6 +22,24 @@
#include <sbi/sbi_vector.h>
#include <sbi/sbi_fp.h>
+#define HART_CTR_FIRST_SELECTOR 0x200
+#define HART_CTR_FROZEN (1UL << 31)
+
+struct hart_ctr_entry {
+ unsigned long source;
+ unsigned long target;
+ unsigned long data;
+};
+
+struct hart_ctr_context {
+ unsigned long ctl;
+ unsigned long depth;
+ unsigned long status;
+ unsigned long vsctl;
+ unsigned int num_entries;
+ struct hart_ctr_entry entries[];
+};
+
struct hart_context_hext {
unsigned long hstatus;
unsigned long hedeleg;
@@ -142,6 +160,92 @@ static void hart_context_set(struct sbi_domain *dom, u32 hartindex,
hart_context_get(sbi_domain_thishart_ptr(), \
current_hartindex())
+#if __riscv_xlen == 64
+static unsigned int hart_ctr_entry_count(unsigned long depth)
+{
+ unsigned int encoding = depth & 7;
+
+ if (encoding > 4)
+ return 0;
+ return 16U << encoding;
+}
+
+static int hart_ctr_ensure_capacity(struct hart_context *ctx)
+{
+ struct hart_ctr_context *old_ctx = ctx->ctr_ctx;
+ struct hart_ctr_context *new_ctx;
+ unsigned int count;
+
+ count = hart_ctr_entry_count(csr_read(CSR_SCTRDEPTH));
+ if (!count)
+ return SBI_EINVAL;
+
+ if (count <= old_ctx->num_entries)
+ return 0;
+
+ new_ctx = sbi_zalloc(sizeof(*new_ctx) +
+ count * sizeof(*new_ctx->entries));
+ if (!new_ctx)
+ return SBI_ENOMEM;
+
+ /*
+ * The outgoing CTR state remains in hardware until the switch.
+ */
+ new_ctx->num_entries = count;
+
+ ctx->ctr_ctx = new_ctx;
+ sbi_free(old_ctx);
+
+ return 0;
+}
+
+static void hart_ctr_save(struct hart_ctr_context *ctx)
+{
+ unsigned int i, count;
+
+ ctx->status = csr_read_set(CSR_SCTRSTATUS, HART_CTR_FROZEN);
+ ctx->ctl = csr_read(CSR_SCTRCTL);
+ ctx->depth = csr_read(CSR_SCTRDEPTH);
+ if (misa_extension('H'))
+ ctx->vsctl = csr_read(CSR_VSCTRCTL);
+
+ count = hart_ctr_entry_count(ctx->depth);
+ if (count > ctx->num_entries)
+ count = ctx->num_entries;
+ for (i = 0; i < count; i++) {
+ csr_write(CSR_SISELECT, HART_CTR_FIRST_SELECTOR + i);
+ ctx->entries[i].source = csr_read(CSR_SIREG);
+ ctx->entries[i].target = csr_read(CSR_SIREG2);
+ ctx->entries[i].data = csr_read(CSR_SIREG3);
+ }
+}
+
+static void hart_ctr_restore(const struct hart_ctr_context *ctx)
+{
+ unsigned int i, count;
+
+ csr_write(CSR_SCTRCTL, 0);
+ csr_write(CSR_SCTRDEPTH, ctx->depth);
+ __asm__ __volatile__(".4byte 0x10400073" ::: "memory");
+ csr_write(CSR_SCTRSTATUS, ctx->status | HART_CTR_FROZEN);
+
+ count = hart_ctr_entry_count(ctx->depth);
+ if (count > ctx->num_entries)
+ count = ctx->num_entries;
+ for (i = 0; i < count; i++) {
+ csr_write(CSR_SISELECT, HART_CTR_FIRST_SELECTOR + i);
+ csr_write(CSR_SIREG2, ctx->entries[i].target);
+ csr_write(CSR_SIREG3, ctx->entries[i].data);
+ csr_write(CSR_SIREG, ctx->entries[i].source);
+ }
+
+ if (misa_extension('H'))
+ csr_write(CSR_VSCTRCTL, ctx->vsctl);
+ csr_write(CSR_SCTRCTL, ctx->ctl);
+ csr_write(CSR_SCTRSTATUS, ctx->status);
+}
+#endif
+
/**
* Switches the HART context from the current domain to the target domain.
* This includes changing domain assignments and reconfiguring PMP, as well
@@ -293,7 +397,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCCFG) &&
(csr_read64(CSR_MENVCFG) & ENVCFG_CDE))
ctx->scountinhibit = csr_swap(CSR_SCOUNTINHIBIT,
- dom_ctx->scountinhibit);
+ dom_ctx->scountinhibit);
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCSRIND) ||
has_smaia) {
ctx->siselect = csr_swap(CSR_SISELECT, dom_ctx->siselect);
@@ -301,6 +405,13 @@ static int switch_to_next_domain_context(struct hart_context *ctx,
ctx->hext.vsiselect = csr_swap(CSR_VSISELECT,
dom_ctx->hext.vsiselect);
}
+#if __riscv_xlen == 64
+ if (ctx->ctr_ctx && dom_ctx->ctr_ctx) {
+ hart_ctr_save(ctx->ctr_ctx);
+ hart_ctr_restore(dom_ctx->ctr_ctx);
+ csr_write(CSR_SISELECT, dom_ctx->siselect);
+ }
+#endif
/* Eager context switch for float */
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_F) ||
@@ -351,6 +462,10 @@ static int hart_context_init(u32 hartindex)
{
size_t vec_size;
struct hart_context *ctx;
+#if __riscv_xlen == 64
+ struct sbi_hart_features *hart_features =
+ sbi_hart_features_ptr(sbi_scratch_thishart_ptr());
+#endif
struct sbi_domain *dom;
sbi_domain_for_each(dom) {
@@ -362,6 +477,19 @@ static int hart_context_init(u32 hartindex)
if (!ctx)
return SBI_ENOMEM;
+#if __riscv_xlen == 64
+ if (hart_features->ctr_num_entries) {
+ ctx->ctr_ctx = sbi_zalloc(sizeof(*ctx->ctr_ctx) +
+ hart_features->ctr_num_entries * sizeof(struct hart_ctr_entry));
+ if (!ctx->ctr_ctx) {
+ sbi_free(ctx);
+ return SBI_ENOMEM;
+ }
+ ctx->ctr_ctx->depth = hart_features->ctr_depth;
+ ctx->ctr_ctx->num_entries = hart_features->ctr_num_entries;
+ }
+#endif
+
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(),
SBI_HART_EXT_V)) {
vec_size = sbi_vector_context_size();
@@ -369,6 +497,7 @@ static int hart_context_init(u32 hartindex)
/* Allocate the vector context pointer */
ctx->vec_ctx = sbi_zalloc(vec_size);
if (!ctx->vec_ctx) {
+ sbi_free(ctx->ctr_ctx);
sbi_free(ctx);
return SBI_ENOMEM;
}
@@ -417,6 +546,14 @@ int sbi_domain_context_enter(struct sbi_domain *dom)
if (!dom_ctx)
return SBI_EINVAL;
+#if __riscv_xlen == 64
+ if (ctx->ctr_ctx && dom_ctx->ctr_ctx) {
+ rc = hart_ctr_ensure_capacity(ctx);
+ if (rc)
+ return rc;
+ }
+#endif
+
/* Update target context's previous context to indicate the caller */
dom_ctx->prev_ctx = ctx;
@@ -446,6 +583,14 @@ int sbi_domain_context_exit(void)
return SBI_EINVAL;
}
+#if __riscv_xlen == 64
+ if (ctx->ctr_ctx) {
+ rc = hart_ctr_ensure_capacity(ctx);
+ if (rc)
+ return rc;
+ }
+#endif
+
dom_ctx = ctx->prev_ctx;
ctx->prev_ctx = NULL;
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index d1f5500d..2a43fcdf 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -696,6 +696,17 @@ __pmp_skip:
__sbi_hart_update_extension(hfeatures,
SBI_HART_EXT_ZIHPM, true);
+#if __riscv_xlen == 64
+ if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCTR) &&
+ sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCSRIND)) {
+ val = csr_read_allowed(CSR_SCTRDEPTH, &trap);
+ if (!trap.cause && (val & 7) <= 4) {
+ hfeatures->ctr_depth = val;
+ hfeatures->ctr_num_entries = 16U << (val & 7);
+ }
+ }
+#endif
+
/* Mark hart feature detection done */
hfeatures->detected = true;
--
2.43.0
More information about the opensbi
mailing list