From 58f1a612e8c121a46a7c30b9a7434e52aedac2a7 Mon Sep 17 00:00:00 2001 From: Jose Martins Date: Tue, 20 Apr 2021 22:36:56 +0100 Subject: [PATCH] target/riscv: fix wfi exception behavior The wfi exception trigger behavior should take into account user mode, hstatus.vtw, and the fact the an wfi might raise different types of exceptions depending on various factors: If supervisor mode is not present: - an illegal instruction exception should be generated if user mode executes and wfi instruction and mstatus.tw = 1. If supervisor mode is present: - when a wfi instruction is executed, an illegal exception should be triggered if either the current mode is user or the mode is supervisor and mstatus.tw is set. Plus, if the hypervisor extensions are enabled: - a virtual instruction exception should be raised when a wfi is executed from virtual-user or virtual-supervisor and hstatus.vtw is set. Signed-off-by: Jose Martins Reviewed-by: Alistair Francis Message-id: 20210420213656.85148-1-josemartins90@gmail.com Signed-off-by: Alistair Francis --- qemu/target/riscv/cpu_bits.h | 1 + qemu/target/riscv/op_helper.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qemu/target/riscv/cpu_bits.h b/qemu/target/riscv/cpu_bits.h index f40ea261..ffa73864 100644 --- a/qemu/target/riscv/cpu_bits.h +++ b/qemu/target/riscv/cpu_bits.h @@ -427,6 +427,7 @@ #define HSTATUS_SP2P 0x00000100 #define HSTATUS_SP2V 0x00000200 #define HSTATUS_VTVM 0x00100000 +#define HSTATUS_VTW 0x00200000 #define HSTATUS_VTSR 0x00400000 #define HSTATUS32_WPRI 0xFF8FF87E diff --git a/qemu/target/riscv/op_helper.c b/qemu/target/riscv/op_helper.c index 05dd67ae..5afb2ce8 100644 --- a/qemu/target/riscv/op_helper.c +++ b/qemu/target/riscv/op_helper.c @@ -172,11 +172,15 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); + bool rvs = riscv_has_ext(env, RVS); + bool prv_u = env->priv == PRV_U; + bool prv_s = env->priv == PRV_S; - if ((env->priv == PRV_S && - env->priv_ver >= PRIV_VERSION_1_10_0 && - get_field(env->mstatus, MSTATUS_TW)) || - riscv_cpu_virt_enabled(env)) { + if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || + (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else if (riscv_cpu_virt_enabled(env) && (prv_u || + (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } else { cs->halted = 1;