aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorJose Martins <josemartins90@gmail.com>2021-10-26 15:51:25 +0100
committerAlistair Francis <alistair@alistair23.me>2021-10-29 16:54:45 +1000
commit487a99551ae903fc83a878d4cbc6d853e17ad252 (patch)
tree953376802ac16489cbbfe787b3c0e65531682920 /target
parent0ee9a4e57e1cee4577cab22f6ced6c0c34fb2d94 (diff)
target/riscv: fix VS interrupts forwarding to HS
VS interrupts (2, 6, 10) were not correctly forwarded to hs-mode when not delegated in hideleg (which was not being taken into account). This was mainly because hs level sie was not always considered enabled when it should. The spec states that "Interrupts for higher-privilege modes, y>x, are always globally enabled regardless of the setting of the global yIE bit for the higher-privilege mode." and also "For purposes of interrupt global enables, HS-mode is considered more privileged than VS-mode, and VS-mode is considered more privileged than VU-mode". Also, vs-level interrupts were not being taken into account unless V=1, but should be unless delegated. Finally, there is no need for a special case for to handle vs interrupts as the current privilege level, the state of the global ie and of the delegation registers should be enough to route all interrupts to the appropriate privilege level in riscv_cpu_do_interrupt. Signed-off-by: Jose Martins <josemartins90@gmail.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-id: 20211026145126.11025-2-josemartins90@gmail.com Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Diffstat (limited to 'target')
-rw-r--r--target/riscv/cpu_helper.c28
1 files changed, 8 insertions, 20 deletions
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 662228c238..5076580374 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -135,36 +135,24 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
#ifndef CONFIG_USER_ONLY
static int riscv_cpu_local_irq_pending(CPURISCVState *env)
{
- target_ulong irqs;
+ target_ulong virt_enabled = riscv_cpu_virt_enabled(env);
target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE);
- target_ulong hs_mstatus_sie = get_field(env->mstatus_hs, MSTATUS_SIE);
- target_ulong pending = env->mip & env->mie &
- ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP);
- target_ulong vspending = (env->mip & env->mie &
- (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP));
+ target_ulong pending = env->mip & env->mie;
target_ulong mie = env->priv < PRV_M ||
(env->priv == PRV_M && mstatus_mie);
target_ulong sie = env->priv < PRV_S ||
(env->priv == PRV_S && mstatus_sie);
- target_ulong hs_sie = env->priv < PRV_S ||
- (env->priv == PRV_S && hs_mstatus_sie);
+ target_ulong hsie = virt_enabled || sie;
+ target_ulong vsie = virt_enabled && sie;
- if (riscv_cpu_virt_enabled(env)) {
- target_ulong pending_hs_irq = pending & -hs_sie;
-
- if (pending_hs_irq) {
- riscv_cpu_set_force_hs_excep(env, FORCE_HS_EXCEP);
- return ctz64(pending_hs_irq);
- }
-
- pending = vspending;
- }
-
- irqs = (pending & ~env->mideleg & -mie) | (pending & env->mideleg & -sie);
+ target_ulong irqs =
+ (pending & ~env->mideleg & -mie) |
+ (pending & env->mideleg & ~env->hideleg & -hsie) |
+ (pending & env->mideleg & env->hideleg & -vsie);
if (irqs) {
return ctz64(irqs); /* since non-zero */