target/riscv: Combine set_mode and set_virt functions.

Combining riscv_cpu_set_virt_enabled() and riscv_cpu_set_mode()
functions. This is to make complete mode change information
available through a single function.

This allows to easily differentiate between HS->VS, VS->HS
and VS->VS transitions when executing state update codes.
For example: One use-case which inspired this change is
to update mode-specific instruction and cycle counters
which requires information of both prev mode and current
mode.

Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Message-ID: <20240711-smcntrpmf_v7-v8-1-b7c38ae7b263@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Rajnesh Kanwal 2024-07-11 15:31:04 -07:00 committed by Alistair Francis
parent 3cb9f20499
commit 68c05fb530
3 changed files with 35 additions and 41 deletions

View File

@ -567,7 +567,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit);
#endif /* !CONFIG_USER_ONLY */
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en);
void riscv_translate_init(void);
G_NORETURN void riscv_raise_exception(CPURISCVState *env,

View File

@ -619,30 +619,6 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen)
env->geilen = geilen;
}
/* This function can only be called to set virt when RVH is enabled */
void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
{
/* Flush the TLB on all virt mode changes. */
if (env->virt_enabled != enable) {
tlb_flush(env_cpu(env));
}
env->virt_enabled = enable;
if (enable) {
/*
* The guest external interrupts from an interrupt controller are
* delivered only when the Guest/VM is running (i.e. V=1). This means
* any guest external interrupt which is triggered while the Guest/VM
* is not running (i.e. V=0) will be missed on QEMU resulting in guest
* with sluggish response to serial console input and other I/O events.
*
* To solve this, we check and inject interrupt after setting V=1.
*/
riscv_cpu_update_mip(env, 0, 0);
}
}
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
{
CPURISCVState *env = &cpu->env;
@ -715,7 +691,7 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
}
}
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en)
{
g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED);
@ -736,6 +712,28 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
* preemptive context switch. As a result, do both.
*/
env->load_res = -1;
if (riscv_has_ext(env, RVH)) {
/* Flush the TLB on all virt mode changes. */
if (env->virt_enabled != virt_en) {
tlb_flush(env_cpu(env));
}
env->virt_enabled = virt_en;
if (virt_en) {
/*
* The guest external interrupts from an interrupt controller are
* delivered only when the Guest/VM is running (i.e. V=1). This
* means any guest external interrupt which is triggered while the
* Guest/VM is not running (i.e. V=0) will be missed on QEMU
* resulting in guest with sluggish response to serial console
* input and other I/O events.
*
* To solve this, we check and inject interrupt after setting V=1.
*/
riscv_cpu_update_mip(env, 0, 0);
}
}
}
/*
@ -1648,6 +1646,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
bool virt = env->virt_enabled;
bool write_gva = false;
uint64_t s;
@ -1778,7 +1777,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
htval = env->guest_phys_fault_addr;
riscv_cpu_set_virt_enabled(env, 0);
virt = false;
} else {
/* Trap into HS mode */
env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false);
@ -1799,7 +1798,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->htinst = tinst;
env->pc = (env->stvec >> 2 << 2) +
((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
riscv_cpu_set_mode(env, PRV_S);
riscv_cpu_set_mode(env, PRV_S, virt);
} else {
/* handle the trap in M-mode */
if (riscv_has_ext(env, RVH)) {
@ -1815,7 +1814,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
mtval2 = env->guest_phys_fault_addr;
/* Trapping to M mode, virt is disabled */
riscv_cpu_set_virt_enabled(env, 0);
virt = false;
}
s = env->mstatus;
@ -1830,7 +1829,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->mtinst = tinst;
env->pc = (env->mtvec >> 2 << 2) +
((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
riscv_cpu_set_mode(env, PRV_M);
riscv_cpu_set_mode(env, PRV_M, virt);
}
/*

View File

@ -264,7 +264,7 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
target_ulong helper_sret(CPURISCVState *env)
{
uint64_t mstatus;
target_ulong prev_priv, prev_virt;
target_ulong prev_priv, prev_virt = env->virt_enabled;
if (!(env->priv >= PRV_S)) {
riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
@ -307,11 +307,9 @@ target_ulong helper_sret(CPURISCVState *env)
if (prev_virt) {
riscv_cpu_swap_hypervisor_regs(env);
}
riscv_cpu_set_virt_enabled(env, prev_virt);
}
riscv_cpu_set_mode(env, prev_priv);
riscv_cpu_set_mode(env, prev_priv, prev_virt);
return retpc;
}
@ -347,16 +345,13 @@ target_ulong helper_mret(CPURISCVState *env)
mstatus = set_field(mstatus, MSTATUS_MPRV, 0);
}
env->mstatus = mstatus;
riscv_cpu_set_mode(env, prev_priv);
if (riscv_has_ext(env, RVH)) {
if (prev_virt) {
riscv_cpu_swap_hypervisor_regs(env);
}
riscv_cpu_set_virt_enabled(env, prev_virt);
if (riscv_has_ext(env, RVH) && prev_virt) {
riscv_cpu_swap_hypervisor_regs(env);
}
riscv_cpu_set_mode(env, prev_priv, prev_virt);
return retpc;
}