Signed-off-by: Alistair Francis <alistair.fran...@wdc.com> --- target/riscv/cpu_bits.h | 4 +-- target/riscv/cpu_helper.c | 71 +++++++++++++++++++++++++++++++++------ target/riscv/csr.c | 4 +-- 3 files changed, 65 insertions(+), 14 deletions(-)
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 5cee72b726..353fc9a24a 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -513,8 +513,8 @@ #define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 #define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 #define RISCV_EXCP_U_ECALL 0x8 -#define RISCV_EXCP_S_ECALL 0x9 -#define RISCV_EXCP_H_ECALL 0xa +#define RISCV_EXCP_HS_ECALL 0x9 +#define RISCV_EXCP_VS_ECALL 0xa #define RISCV_EXCP_M_ECALL 0xb #define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */ #define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */ diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8e8b156fc0..17eec6217b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -668,6 +668,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + target_ulong s; /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide * so we mask off the MSB and separate into trap type and cause. @@ -677,13 +678,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong deleg = async ? env->mideleg : env->medeleg; target_ulong tval = 0; - static const int ecall_cause_map[] = { - [PRV_U] = RISCV_EXCP_U_ECALL, - [PRV_S] = RISCV_EXCP_S_ECALL, - [PRV_H] = RISCV_EXCP_H_ECALL, - [PRV_M] = RISCV_EXCP_M_ECALL - }; - if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { @@ -704,7 +698,16 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* ecall is dispatched as one cause so translate based on mode */ if (cause == RISCV_EXCP_U_ECALL) { assert(env->priv <= 3); - cause = ecall_cause_map[env->priv]; + + if (env->priv == PRV_M) { + cause = RISCV_EXCP_M_ECALL; + } else if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_VS_ECALL; + } else if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_HS_ECALL; + } else if (env->priv == PRV_U) { + cause = RISCV_EXCP_U_ECALL; + } } } @@ -714,7 +717,42 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (env->priv <= PRV_S && cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ - target_ulong s = *env->mstatus; + if (riscv_has_ext(env, RVH)) { + target_ulong hdeleg = async ? env->hideleg : env->hedeleg; + + if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1) && + !riscv_cpu_force_hs_excep_enabled(env)) { + /* Trap to VS mode */ + } else if (riscv_cpu_virt_enabled(env)) { + /* Trap into HS mode, from virt */ + riscv_cpu_swap_hypervisor_regs(env); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(*env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + + if (riscv_cpu_force_hs_excep_enabled(env)) { + env->hstatus = set_field(env->hstatus, HSTATUS_STL, 1); + } else { + env->hstatus = set_field(env->hstatus, HSTATUS_STL, 0); + } + + riscv_cpu_set_virt_enabled(env, VIRT_OFF); + riscv_cpu_set_force_hs_excep(env, CLEAR_HS_EXCEP); + } else { + /* Trap into HS mode */ + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(*env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + } + } + + s = *env->mstatus; s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_SPP, env->priv); @@ -728,7 +766,20 @@ void riscv_cpu_do_interrupt(CPUState *cs) riscv_cpu_set_mode(env, PRV_S); } else { /* handle the trap in M-mode */ - target_ulong s = *env->mstatus; + if (riscv_has_ext(env, RVH)) { + if (riscv_cpu_virt_enabled(env)) { + riscv_cpu_swap_hypervisor_regs(env); + } + *env->mstatus = set_field(*env->mstatus, MSTATUS_MPV, + riscv_cpu_virt_enabled(env)); + *env->mstatus = set_field(*env->mstatus, MSTATUS_MTL, + riscv_cpu_force_hs_excep_enabled(env)); + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_virt_enabled(env, VIRT_OFF); + } + + s = *env->mstatus; s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_MPP, env->priv); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 30ec8c0a8e..47be4b1d42 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -255,8 +255,8 @@ static const target_ulong delegable_excps = (1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS)) | (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) | (1ULL << (RISCV_EXCP_U_ECALL)) | - (1ULL << (RISCV_EXCP_S_ECALL)) | - (1ULL << (RISCV_EXCP_H_ECALL)) | + (1ULL << (RISCV_EXCP_VS_ECALL)) | + (1ULL << (RISCV_EXCP_HS_ECALL)) | (1ULL << (RISCV_EXCP_M_ECALL)) | (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | -- 2.22.0