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:
parent
4923f672e3
commit
53309be156
@ -547,6 +547,7 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen);
|
|||||||
bool riscv_cpu_vector_enabled(CPURISCVState *env);
|
bool riscv_cpu_vector_enabled(CPURISCVState *env);
|
||||||
void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
|
void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
|
||||||
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch);
|
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,
|
G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||||
MMUAccessType access_type,
|
MMUAccessType access_type,
|
||||||
int mmu_idx, uintptr_t retaddr);
|
int mmu_idx, uintptr_t retaddr);
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "cpu_bits.h"
|
#include "cpu_bits.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "tcg/oversized-guest.h"
|
#include "tcg/oversized-guest.h"
|
||||||
|
#include "pmp.h"
|
||||||
|
|
||||||
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
|
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
|
||||||
{
|
{
|
||||||
@ -63,6 +64,33 @@ int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
|
|||||||
#endif
|
#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,
|
void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
|
||||||
uint64_t *cs_base, uint32_t *pflags)
|
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;
|
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));
|
g_assert(riscv_has_ext(env, RVH));
|
||||||
|
|
||||||
if (current_virt) {
|
if (current_virt) {
|
||||||
@ -1769,6 +1806,11 @@ void riscv_cpu_do_interrupt(CPUState *cs)
|
|||||||
if (env->priv <= PRV_S && cause < 64 &&
|
if (env->priv <= PRV_S && cause < 64 &&
|
||||||
(((deleg >> cause) & 1) || s_injected || vs_injected)) {
|
(((deleg >> cause) & 1) || s_injected || vs_injected)) {
|
||||||
/* handle the trap in S-mode */
|
/* 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)) {
|
if (riscv_has_ext(env, RVH)) {
|
||||||
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
|
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);
|
riscv_cpu_set_mode(env, PRV_S, virt);
|
||||||
} else {
|
} else {
|
||||||
/* handle the trap in M-mode */
|
/* 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 (riscv_has_ext(env, RVH)) {
|
||||||
if (env->virt_enabled) {
|
if (env->virt_enabled) {
|
||||||
riscv_cpu_swap_hypervisor_regs(env);
|
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);
|
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
|
* 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
|
* necessary for an SC from "another hart" to cause a load reservation
|
||||||
|
@ -309,6 +309,15 @@ target_ulong helper_sret(CPURISCVState *env)
|
|||||||
|
|
||||||
riscv_cpu_set_mode(env, prev_priv, prev_virt);
|
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;
|
return retpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +358,14 @@ target_ulong helper_mret(CPURISCVState *env)
|
|||||||
}
|
}
|
||||||
|
|
||||||
riscv_cpu_set_mode(env, prev_priv, prev_virt);
|
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;
|
return retpc;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user