target/riscv: save and restore elp state on priv transitions

elp state is recorded in *status on trap entry (less privilege to higher
privilege) and restored in elp from *status on trap exit (higher to less
privilege).

Additionally this patch introduces a forward cfi helper function to
determine if current privilege has forward cfi is enabled or not based on
*envcfg (for U, VU, S, VU, HS) or mseccfg csr (for M).

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Co-developed-by: Jim Shu <jim.shu@sifive.com>
Co-developed-by: Andy Chiu <andy.chiu@sifive.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20241008225010.1861630-5-debug@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Deepak Gupta 2024-10-08 15:49:54 -07:00 committed by Alistair Francis
parent 4923f672e3
commit 53309be156
3 changed files with 72 additions and 0 deletions

View File

@ -547,6 +547,7 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen);
bool riscv_cpu_vector_enabled(CPURISCVState *env);
void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch);
bool cpu_get_fcfien(CPURISCVState *env);
G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr);

View File

@ -33,6 +33,7 @@
#include "cpu_bits.h"
#include "debug.h"
#include "tcg/oversized-guest.h"
#include "pmp.h"
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
{
@ -63,6 +64,33 @@ int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
#endif
}
bool cpu_get_fcfien(CPURISCVState *env)
{
/* no cfi extension, return false */
if (!env_archcpu(env)->cfg.ext_zicfilp) {
return false;
}
switch (env->priv) {
case PRV_U:
if (riscv_has_ext(env, RVS)) {
return env->senvcfg & SENVCFG_LPE;
}
return env->menvcfg & MENVCFG_LPE;
#ifndef CONFIG_USER_ONLY
case PRV_S:
if (env->virt_enabled) {
return env->henvcfg & HENVCFG_LPE;
}
return env->menvcfg & MENVCFG_LPE;
case PRV_M:
return env->mseccfg & MSECCFG_MLPE;
#endif
default:
g_assert_not_reached();
}
}
void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
uint64_t *cs_base, uint32_t *pflags)
{
@ -546,6 +574,15 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
}
bool current_virt = env->virt_enabled;
/*
* If zicfilp extension available and henvcfg.LPE = 1,
* then apply SPELP mask on mstatus
*/
if (env_archcpu(env)->cfg.ext_zicfilp &&
get_field(env->henvcfg, HENVCFG_LPE)) {
mstatus_mask |= SSTATUS_SPELP;
}
g_assert(riscv_has_ext(env, RVH));
if (current_virt) {
@ -1769,6 +1806,11 @@ void riscv_cpu_do_interrupt(CPUState *cs)
if (env->priv <= PRV_S && cause < 64 &&
(((deleg >> cause) & 1) || s_injected || vs_injected)) {
/* handle the trap in S-mode */
/* save elp status */
if (cpu_get_fcfien(env)) {
env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
}
if (riscv_has_ext(env, RVH)) {
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
@ -1818,6 +1860,11 @@ void riscv_cpu_do_interrupt(CPUState *cs)
riscv_cpu_set_mode(env, PRV_S, virt);
} else {
/* handle the trap in M-mode */
/* save elp status */
if (cpu_get_fcfien(env)) {
env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
}
if (riscv_has_ext(env, RVH)) {
if (env->virt_enabled) {
riscv_cpu_swap_hypervisor_regs(env);
@ -1850,6 +1897,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
riscv_cpu_set_mode(env, PRV_M, virt);
}
/*
* Interrupt/exception/trap delivery is asynchronous event and as per
* zicfilp spec CPU should clear up the ELP state. No harm in clearing
* unconditionally.
*/
env->elp = false;
/*
* NOTE: it is not necessary to yield load reservations here. It is only
* necessary for an SC from "another hart" to cause a load reservation

View File

@ -309,6 +309,15 @@ target_ulong helper_sret(CPURISCVState *env)
riscv_cpu_set_mode(env, prev_priv, prev_virt);
/*
* If forward cfi enabled for new priv, restore elp status
* and clear spelp in mstatus
*/
if (cpu_get_fcfien(env)) {
env->elp = get_field(env->mstatus, MSTATUS_SPELP);
}
env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
return retpc;
}
@ -349,6 +358,14 @@ target_ulong helper_mret(CPURISCVState *env)
}
riscv_cpu_set_mode(env, prev_priv, prev_virt);
/*
* If forward cfi enabled for new priv, restore elp status
* and clear mpelp in mstatus
*/
if (cpu_get_fcfien(env)) {
env->elp = get_field(env->mstatus, MSTATUS_MPELP);
}
env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
return retpc;
}