Skip to content

Commit

Permalink
target/riscv: Add M-mode virtual interrupt and IRQ filtering support.
Browse files Browse the repository at this point in the history
This change adds support for inserting virtual interrupts from M-mode
into S-mode using mvien and mvip csrs. IRQ filtering is a use case of
this change, i-e M-mode can stop delegating an interrupt to S-mode and
instead enable it in MIE and receive those interrupts in M-mode and then
selectively inject the interrupt using mvien and mvip.

Also, the spec doesn't mandate the interrupt to be actually supported
in hardware. Which allows M-mode to assert virtual interrupts to S-mode
that have no connection to any real interrupt events.

This is defined as part of the AIA specification [0], "5.3 Interrupt
filtering and virtual interrupts for supervisor level".

[0]: https://github.com/riscv/riscv-aia/releases/download/1.0/riscv-interrupts-1.0.pdf

Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Message-ID: <20231016111736.28721-6-rkanwal@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
  • Loading branch information
rajnesh-kanwal authored and alistair23 committed Nov 7, 2023
1 parent 1ebad50 commit 1697837
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 38 deletions.
3 changes: 2 additions & 1 deletion target/riscv/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,8 @@ static bool riscv_cpu_has_work(CPUState *cs)
* Definition of the WFI instruction requires it to ignore the privilege
* mode and delegation registers, but respect individual enables
*/
return riscv_cpu_all_pending(env) != 0;
return riscv_cpu_all_pending(env) != 0 ||
riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE;
#else
return true;
#endif
Expand Down
8 changes: 8 additions & 0 deletions target/riscv/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ struct CPUArchState {
uint64_t mie;
uint64_t mideleg;

/*
* When mideleg[i]=0 and mvien[i]=1, sie[i] is no more
* alias of mie[i] and needs to be maintained separatly.
*/
uint64_t sie;

target_ulong satp; /* since: priv-1.10.0 */
target_ulong stval;
target_ulong medeleg;
Expand All @@ -222,6 +228,8 @@ struct CPUArchState {
/* AIA CSRs */
target_ulong miselect;
target_ulong siselect;
uint64_t mvien;
uint64_t mvip;

/* Hypervisor CSRs */
target_ulong hstatus;
Expand Down
6 changes: 6 additions & 0 deletions target/riscv/cpu_bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,12 @@ typedef enum RISCVException {
#define MIE_SSIE (1 << IRQ_S_SOFT)
#define MIE_USIE (1 << IRQ_U_SOFT)

/* Machine constants */
#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP))
#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP))
#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP))
#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS))

/* General PointerMasking CSR bits */
#define PM_ENABLE 0x00000001ULL
#define PM_CURRENT 0x00000002ULL
Expand Down
26 changes: 19 additions & 7 deletions target/riscv/cpu_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env,
return best_irq;
}

/*
* Doesn't report interrupts inserted using mvip from M-mode firmware. Those
* are returned in riscv_cpu_sirq_pending().
*/
uint64_t riscv_cpu_all_pending(CPURISCVState *env)
{
uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
Expand All @@ -398,9 +402,10 @@ int riscv_cpu_sirq_pending(CPURISCVState *env)
{
uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg &
~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP);
uint64_t irqs_f = env->mvip & env->mvien & ~env->mideleg & env->sie;

return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S,
irqs, env->siprio);
irqs | irqs_f, env->siprio);
}

int riscv_cpu_vsirq_pending(CPURISCVState *env)
Expand All @@ -414,8 +419,8 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env)

static int riscv_cpu_local_irq_pending(CPURISCVState *env)
{
uint64_t irqs, pending, mie, hsie, vsie, irqs_f;
int virq;
uint64_t irqs, pending, mie, hsie, vsie;

/* Determine interrupt enable state of all privilege modes */
if (env->virt_enabled) {
Expand All @@ -441,8 +446,11 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env)
irqs, env->miprio);
}

/* Check for virtual S-mode interrupts. */
irqs_f = env->mvip & (env->mvien & ~env->mideleg) & env->sie;

/* Check HS-mode interrupts */
irqs = pending & env->mideleg & ~env->hideleg & -hsie;
irqs = ((pending & env->mideleg & ~env->hideleg) | irqs_f) & -hsie;
if (irqs) {
return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S,
irqs, env->siprio);
Expand Down Expand Up @@ -622,19 +630,21 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)

void riscv_cpu_interrupt(CPURISCVState *env)
{
uint64_t gein, vsgein = 0, vstip = 0;
uint64_t gein, vsgein = 0, vstip = 0, irqf = 0;
CPUState *cs = env_cpu(env);

QEMU_IOTHREAD_LOCK_GUARD();

if (env->virt_enabled) {
gein = get_field(env->hstatus, HSTATUS_VGEIN);
vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
} else {
irqf = env->mvien & env->mvip & env->sie;
}

vstip = env->vstime_irq ? MIP_VSTIP : 0;

if (env->mip | vsgein | vstip) {
if (env->mip | vsgein | vstip | irqf) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
Expand Down Expand Up @@ -1611,6 +1621,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG);
target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
uint64_t deleg = async ? env->mideleg : env->medeleg;
bool s_injected = env->mvip & (1 << cause) & env->mvien &&
!(env->mip & (1 << cause));
target_ulong tval = 0;
target_ulong tinst = 0;
target_ulong htval = 0;
Expand Down Expand Up @@ -1699,8 +1711,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
__func__, env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));

if (env->priv <= PRV_S &&
cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
if (env->priv <= PRV_S && cause < 64 &&
(((deleg >> cause) & 1) || s_injected)) {
/* handle the trap in S-mode */
if (riscv_has_ext(env, RVH)) {
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
Expand Down

0 comments on commit 1697837

Please sign in to comment.