blob: 7528591805dc381086d09c0e7835842bfad26608 [file] [log] [blame]
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) IPADS@SJTU 2023. All rights reserved.
*/
#include <sbi/sbi_error.h>
#include <sbi/riscv_locks.h>
#include <sbi/riscv_asm.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_hsm.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_heap.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_domain_context.h>
/**
* Switches the HART context from the current domain to the target domain.
* This includes changing domain assignments and reconfiguring PMP, as well
* as saving and restoring CSRs and trap states.
*
* @param ctx pointer to the current HART context
* @param dom_ctx pointer to the target domain context
*/
static void switch_to_next_domain_context(struct sbi_context *ctx,
struct sbi_context *dom_ctx)
{
u32 hartindex = sbi_hartid_to_hartindex(current_hartid());
struct sbi_trap_regs *trap_regs;
struct sbi_domain *current_dom = ctx->dom;
struct sbi_domain *target_dom = dom_ctx->dom;
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
unsigned int pmp_count = sbi_hart_pmp_count(scratch);
/* Assign current hart to target domain */
spin_lock(&current_dom->assigned_harts_lock);
sbi_hartmask_clear_hartindex(hartindex, &current_dom->assigned_harts);
spin_unlock(&current_dom->assigned_harts_lock);
sbi_update_hartindex_to_domain(hartindex, target_dom);
spin_lock(&target_dom->assigned_harts_lock);
sbi_hartmask_set_hartindex(hartindex, &target_dom->assigned_harts);
spin_unlock(&target_dom->assigned_harts_lock);
/* Reconfigure PMP settings for the new domain */
for (int i = 0; i < pmp_count; i++) {
pmp_disable(i);
}
sbi_hart_pmp_configure(scratch);
/* 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);
ctx->stvec = csr_swap(CSR_STVEC, dom_ctx->stvec);
ctx->sscratch = csr_swap(CSR_SSCRATCH, dom_ctx->sscratch);
ctx->sepc = csr_swap(CSR_SEPC, dom_ctx->sepc);
ctx->scause = csr_swap(CSR_SCAUSE, dom_ctx->scause);
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 (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)
ctx->senvcfg = csr_swap(CSR_SENVCFG, dom_ctx->senvcfg);
/* Save current trap state and restore target domain's trap state */
trap_regs = (struct sbi_trap_regs *)(csr_read(CSR_MSCRATCH) -
SBI_TRAP_REGS_SIZE);
sbi_memcpy(&ctx->regs, trap_regs, sizeof(*trap_regs));
sbi_memcpy(trap_regs, &dom_ctx->regs, sizeof(*trap_regs));
/* Mark current context structure initialized because context saved */
ctx->initialized = true;
/* If target domain context is not initialized or runnable */
if (!dom_ctx->initialized) {
/* Startup boot HART of target domain */
if (current_hartid() == target_dom->boot_hartid)
sbi_hart_switch_mode(target_dom->boot_hartid,
target_dom->next_arg1,
target_dom->next_addr,
target_dom->next_mode,
false);
else
sbi_hsm_hart_stop(scratch, true);
}
}
int sbi_domain_context_enter(struct sbi_domain *dom)
{
struct sbi_context *ctx = sbi_domain_context_thishart_ptr();
struct sbi_context *dom_ctx = sbi_hartindex_to_domain_context(
sbi_hartid_to_hartindex(current_hartid()), dom);
/* Validate the domain context existence */
if (!dom_ctx)
return SBI_EINVAL;
/* Update target context's previous context to indicate the caller */
dom_ctx->prev_ctx = ctx;
switch_to_next_domain_context(ctx, dom_ctx);
return 0;
}
int sbi_domain_context_exit(void)
{
u32 i, hartindex = sbi_hartid_to_hartindex(current_hartid());
struct sbi_domain *dom;
struct sbi_context *ctx = sbi_domain_context_thishart_ptr();
struct sbi_context *dom_ctx, *tmp;
/*
* If it's first time to call `exit` on the current hart, no
* context allocated before. Loop through each domain to allocate
* its context on the current hart if valid.
*/
if (!ctx) {
sbi_domain_for_each(i, dom) {
if (!sbi_hartmask_test_hartindex(hartindex,
dom->possible_harts))
continue;
dom_ctx = sbi_zalloc(sizeof(struct sbi_context));
if (!dom_ctx)
return SBI_ENOMEM;
/* Bind context and domain */
dom_ctx->dom = dom;
dom->hartindex_to_context_table[hartindex] = dom_ctx;
}
ctx = sbi_domain_context_thishart_ptr();
}
dom_ctx = ctx->prev_ctx;
/* If no previous caller context */
if (!dom_ctx) {
/* Try to find next uninitialized user-defined domain's context */
sbi_domain_for_each(i, dom) {
if (dom == &root || dom == sbi_domain_thishart_ptr())
continue;
tmp = sbi_hartindex_to_domain_context(hartindex, dom);
if (tmp && !tmp->initialized) {
dom_ctx = tmp;
break;
}
}
}
/* Take the root domain context if fail to find */
if (!dom_ctx)
dom_ctx = sbi_hartindex_to_domain_context(hartindex, &root);
switch_to_next_domain_context(ctx, dom_ctx);
return 0;
}