target/riscv: Implement Ssdbltrp sret, mret and mnret behavior

When the Ssdbltrp extension is enabled, SSTATUS.SDT field is cleared
when executing sret. When executing mret/mnret, SSTATUS.SDT is cleared
when returning to U, VS or VU and VSSTATUS.SDT is cleared when returning
to VU from HS.

Signed-off-by: Clément Léger <cleger@rivosinc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250110125441.3208676-4-cleger@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Clément Léger 2025-01-10 13:54:34 +01:00 committed by Alistair Francis
parent 0aadf8162a
commit 72d71d8732

View File

@ -294,6 +294,18 @@ target_ulong helper_sret(CPURISCVState *env)
get_field(mstatus, MSTATUS_SPIE)); get_field(mstatus, MSTATUS_SPIE));
mstatus = set_field(mstatus, MSTATUS_SPIE, 1); mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
if (riscv_cpu_cfg(env)->ext_ssdbltrp) {
if (riscv_has_ext(env, RVH)) {
target_ulong prev_vu = get_field(env->hstatus, HSTATUS_SPV) &&
prev_priv == PRV_U;
/* Returning to VU from HS, vsstatus.sdt = 0 */
if (!env->virt_enabled && prev_vu) {
env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0);
}
}
mstatus = set_field(mstatus, MSTATUS_SDT, 0);
}
if (env->priv_ver >= PRIV_VERSION_1_12_0) { if (env->priv_ver >= PRIV_VERSION_1_12_0) {
mstatus = set_field(mstatus, MSTATUS_MPRV, 0); mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
} }
@ -304,7 +316,6 @@ target_ulong helper_sret(CPURISCVState *env)
target_ulong hstatus = env->hstatus; target_ulong hstatus = env->hstatus;
prev_virt = get_field(hstatus, HSTATUS_SPV); prev_virt = get_field(hstatus, HSTATUS_SPV);
hstatus = set_field(hstatus, HSTATUS_SPV, 0); hstatus = set_field(hstatus, HSTATUS_SPV, 0);
env->hstatus = hstatus; env->hstatus = hstatus;
@ -344,6 +355,22 @@ static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc,
riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC());
} }
} }
static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus,
target_ulong prev_priv,
target_ulong prev_virt)
{
/* If returning to U, VS or VU, sstatus.sdt = 0 */
if (prev_priv == PRV_U || (prev_virt &&
(prev_priv == PRV_S || prev_priv == PRV_U))) {
mstatus = set_field(mstatus, MSTATUS_SDT, 0);
/* If returning to VU, vsstatus.sdt = 0 */
if (prev_virt && prev_priv == PRV_U) {
env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0);
}
}
return mstatus;
}
target_ulong helper_mret(CPURISCVState *env) target_ulong helper_mret(CPURISCVState *env)
{ {
@ -361,6 +388,9 @@ target_ulong helper_mret(CPURISCVState *env)
mstatus = set_field(mstatus, MSTATUS_MPP, mstatus = set_field(mstatus, MSTATUS_MPP,
riscv_has_ext(env, RVU) ? PRV_U : PRV_M); riscv_has_ext(env, RVU) ? PRV_U : PRV_M);
mstatus = set_field(mstatus, MSTATUS_MPV, 0); mstatus = set_field(mstatus, MSTATUS_MPV, 0);
if (riscv_cpu_cfg(env)->ext_ssdbltrp) {
mstatus = ssdbltrp_mxret(env, mstatus, prev_priv, prev_virt);
}
if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) {
mstatus = set_field(mstatus, MSTATUS_MPRV, 0); mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
} }
@ -402,6 +432,9 @@ target_ulong helper_mnret(CPURISCVState *env)
if (prev_priv < PRV_M) { if (prev_priv < PRV_M) {
env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false); env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false);
} }
if (riscv_cpu_cfg(env)->ext_ssdbltrp) {
env->mstatus = ssdbltrp_mxret(env, env->mstatus, prev_priv, prev_virt);
}
if (riscv_has_ext(env, RVH) && prev_virt) { if (riscv_has_ext(env, RVH) && prev_virt) {
riscv_cpu_swap_hypervisor_regs(env); riscv_cpu_swap_hypervisor_regs(env);