Message ID | 20230518113838.130084-7-rkanwal@rivosinc.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add RISC-V Virtual IRQs and IRQ filtering support | expand |
On 5/18/23 08:38, Rajnesh Kanwal wrote: > This change adds support for inserting virtual interrupts from HS-mode > into VS-mode using hvien and hvip csrs. This also allows for IRQ filtering > from HS-mode. > > Also, the spec doesn't mandate the interrupt to be actually supported > in hardware. Which allows HS-mode to assert virtual interrupts to VS-mode > that have no connection to any real interrupt events. > > This is defined as part of the AIA specification [0], "6.3.2 Virtual > interrupts for VS level". > > [0]: https://github.com/riscv/riscv-aia/releases/download/1.0-RC4/riscv-interrupts-1.0-RC4.pdf > > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> > --- > target/riscv/cpu.c | 3 +- > target/riscv/cpu.h | 14 +++ > target/riscv/cpu_helper.c | 48 +++++++--- > target/riscv/csr.c | 196 ++++++++++++++++++++++++++++++++++---- > target/riscv/machine.c | 3 + > 5 files changed, 234 insertions(+), 30 deletions(-) > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > index 9557194a21..c2b05d4c37 100644 > --- a/target/riscv/cpu.c > +++ b/target/riscv/cpu.c > @@ -713,7 +713,8 @@ static bool riscv_cpu_has_work(CPUState *cs) > * mode and delegation registers, but respect individual enables > */ > return riscv_cpu_all_pending(env) != 0 || > - riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE; > + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || > + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; > #else > return true; > #endif > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > index 07cf656471..3e10eee38f 100644 > --- a/target/riscv/cpu.h > +++ b/target/riscv/cpu.h > @@ -196,6 +196,12 @@ struct CPUArchState { > */ > uint64_t sie; > > + /* > + * When hideleg[i]=0 and hvien[i]=1, vsie[i] is no more > + * alias of sie[i] (mie[i]) and needs to be maintained separatly. > + */ > + uint64_t vsie; > + > target_ulong satp; /* since: priv-1.10.0 */ > target_ulong stval; > target_ulong medeleg; > @@ -230,6 +236,14 @@ struct CPUArchState { > target_ulong hgeie; > target_ulong hgeip; > uint64_t htimedelta; > + uint64_t hvien; > + > + /* > + * Bits VSSIP, VSTIP and VSEIP in hvip are maintained in mip. Other bits > + * from 0:12 are reserved. Bits 13:63 are not aliased and must be separately > + * maintain in hvip. > + */ > + uint64_t hvip; > > /* Hypervisor controlled virtual interrupt priorities */ > target_ulong hvictl; > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c > index 681b4ae811..80bdd4cf5a 100644 > --- a/target/riscv/cpu_helper.c > +++ b/target/riscv/cpu_helper.c > @@ -366,8 +366,9 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, > } > > /* > - * Doesn't report interrupts inserted using mvip from M-mode firmware. Those > - * are returned in riscv_cpu_sirq_pending(). > + * Doesn't report interrupts inserted using mvip from M-mode firmware or > + * using hvip bits 13:63 from HS-mode. Those are returned in > + * riscv_cpu_sirq_pending() and riscv_cpu_vsirq_pending(). > */ > uint64_t riscv_cpu_all_pending(CPURISCVState *env) > { > @@ -399,16 +400,23 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) > > int riscv_cpu_vsirq_pending(CPURISCVState *env) > { > - uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & > - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); > + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & env->hideleg; > + uint64_t irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; > + uint64_t vsbits; > + > + /* Bring VS-level bits to correct position */ > + vsbits = irqs & VS_MODE_INTERRUPTS; > + irqs &= ~VS_MODE_INTERRUPTS; > + irqs |= vsbits >> 1; > > return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, > - irqs >> 1, env->hviprio); > + (irqs | irqs_f_vs), env->hviprio); > } > > static int riscv_cpu_local_irq_pending(CPURISCVState *env) > { > - uint64_t irqs, pending, mie, hsie, vsie, irqs_f; > + uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; > + uint64_t vsbits, irq_delegated; > int virq; > > /* Determine interrupt enable state of all privilege modes */ > @@ -445,12 +453,26 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) > irqs, env->siprio); > } > > + /* Check for virtual VS-mode interrupts. */ > + irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; > + > /* Check VS-mode interrupts */ > - irqs = pending & env->mideleg & env->hideleg & -vsie; > + irq_delegated = pending & env->mideleg & env->hideleg; > + > + /* Bring VS-level bits to correct position */ > + vsbits = irq_delegated & VS_MODE_INTERRUPTS; > + irq_delegated &= ~VS_MODE_INTERRUPTS; > + irq_delegated |= vsbits >> 1; > + > + irqs = (irq_delegated | irqs_f_vs) & -vsie; > if (irqs) { > virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, > - irqs >> 1, env->hviprio); > - return (virq <= 0) ? virq : virq + 1; > + irqs, env->hviprio); > + if (virq <= 0 || (virq > 12 && virq <= 63)) { > + return virq; > + } else { > + return virq + 1; > + } > } > > /* Indicate no pending interrupt */ > @@ -625,6 +647,7 @@ void riscv_cpu_interrupt(CPURISCVState *env) > if (env->virt_enabled) { > gein = get_field(env->hstatus, HSTATUS_VGEIN); > vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; > + irqf = env->hvien & env->hvip & env->vsie; > } else { > irqf = env->mvien & env->mvip & env->sie; > } > @@ -1620,6 +1643,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) > uint64_t deleg = async ? env->mideleg : env->medeleg; > bool s_injected = env->mvip & (1 << cause) & env->mvien && > !(env->mip & (1 << cause)); > + bool vs_injected = env->hvip & (1 << cause) & env->hvien && > + !(env->mip & (1 << cause)); > target_ulong tval = 0; > target_ulong tinst = 0; > target_ulong htval = 0; > @@ -1711,12 +1736,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) > riscv_cpu_get_trap_name(cause, async)); > > if (env->priv <= PRV_S && cause < 64 && > - (((deleg >> cause) & 1) || s_injected)) { > + (((deleg >> cause) & 1) || s_injected || vs_injected)) { > /* handle the trap in S-mode */ > if (riscv_has_ext(env, RVH)) { > uint64_t hdeleg = async ? env->hideleg : env->hedeleg; > > - if (env->virt_enabled && ((hdeleg >> cause) & 1)) { > + if (env->virt_enabled && > + (((hdeleg >> cause) & 1) || vs_injected)) { > /* Trap to VS mode */ > /* > * See if we need to adjust cause. Yes if its VS mode interrupt > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > index c1ca065a81..1929d5fa7b 100644 > --- a/target/riscv/csr.c > +++ b/target/riscv/csr.c > @@ -30,6 +30,11 @@ > #include "qemu/guest-random.h" > #include "qapi/error.h" > > + > +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, > + uint64_t *ret_val, > + uint64_t new_val, uint64_t wr_mask); > + This forward declaration breaks qemu linux-user build: [2/26] Compiling C object libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o FAILED: libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o cc -m64 -mcx16 -Ilibqemu-riscv32-linux-user.fa.p -I. -I.. -Itarget/riscv -I../target/riscv -I../common-user/host/x86_64 -I../linux-user/include/host/x86_64 -I../linux-user/include -Ilinux-user -I../linux-user -I../linux-user/riscv -Iqapi -Itrace -Iui -Iui/shader -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/sysprof-4 -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O2 -g -isystem /home/danielhb/work/qemu/linux-headers -isystem linux-headers -iquote . -iquote /home/danielhb/work/qemu -iquote /home/danielhb/work/qemu/include -iquote /home/danielhb/work/qemu/tcg/i386 -pthread -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -Wundef -Wwrite-strings -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wold-style-declaration -Wold-style-definition -Wtype-limits -Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers -Wempty-body -Wnested-externs -Wendif-labels -Wexpansion-to-defined -Wimplicit-fallthrough=2 -Wmissing-format-attribute -Wno-missing-include-dirs -Wno-shift-negative-value -Wno-psabi -fstack-protector-strong -fPIE -isystem../linux-headers -isystemlinux-headers -DNEED_CPU_H '-DCONFIG_TARGET="riscv32-linux-user-config-target.h"' '-DCONFIG_DEVICES="riscv32-linux-user-config-devices.h"' -MD -MQ libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o -MF libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o.d -o libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o -c ../target/riscv/csr.c ../target/riscv/csr.c:34:23: error: ‘rmw_hvip64’ declared ‘static’ but never defined [-Werror=unused-function] 34 | static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, | ^~~~~~~~~~ cc1: all warnings being treated as errors It's not clear in the code but rmw_vsip64() is inside a huge "#if defined(CONFIG_USER_ONLY)" block that starts at line 775. Putting "rmw_hvip64" forward declaration right before rmw_vsip64() is enough to fix it. Thanks, Daniel > /* CSR function table public API */ > void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) > { > @@ -1176,6 +1181,8 @@ static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; > static const target_ulong hip_writable_mask = MIP_VSSIP; > static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | > MIP_VSEIP | LOCAL_INTERRUPTS; > +static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; > + > static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; > > const bool valid_vm_1_10_32[16] = { > @@ -2584,16 +2591,36 @@ static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, > uint64_t *ret_val, > uint64_t new_val, uint64_t wr_mask) > { > + uint64_t alias_mask = (LOCAL_INTERRUPTS | VS_MODE_INTERRUPTS) & > + env->hideleg; > + uint64_t nalias_mask = LOCAL_INTERRUPTS & (~env->hideleg & env->hvien); > + uint64_t rval, rval_vs, vsbits; > + uint64_t wr_mask_vsie; > + uint64_t wr_mask_mie; > RISCVException ret; > - uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; > > /* Bring VS-level bits to correct position */ > - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; > - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; > + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); > + new_val &= ~(VS_MODE_INTERRUPTS >> 1); > + new_val |= vsbits << 1; > + > + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); > + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); > + wr_mask |= vsbits << 1; > + > + wr_mask_mie = wr_mask & alias_mask; > + wr_mask_vsie = wr_mask & nalias_mask; > + > + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask_mie); > + > + rval_vs = env->vsie & nalias_mask; > + env->vsie = (env->vsie & ~wr_mask_vsie) | (new_val & wr_mask_vsie); > > - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); > if (ret_val) { > - *ret_val = (rval & mask) >> 1; > + rval &= alias_mask; > + vsbits = rval & VS_MODE_INTERRUPTS; > + rval &= ~VS_MODE_INTERRUPTS; > + *ret_val = rval | (vsbits >> 1) | rval_vs; > } > > return ret; > @@ -2812,15 +2839,26 @@ static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, > { > RISCVException ret; > uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; > + uint64_t vsbits; > > - /* Bring VS-level bits to correct position */ > - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; > - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; > + /* Add virtualized bits into vsip mask. */ > + mask |= env->hvien & ~env->hideleg; > > - ret = rmw_mip64(env, csrno, &rval, new_val, > - wr_mask & mask & vsip_writable_mask); > + /* Bring VS-level bits to correct position */ > + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); > + new_val &= ~(VS_MODE_INTERRUPTS >> 1); > + new_val |= vsbits << 1; > + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); > + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); > + wr_mask |= vsbits << 1; > + > + ret = rmw_hvip64(env, csrno, &rval, new_val, > + wr_mask & mask & vsip_writable_mask); > if (ret_val) { > - *ret_val = (rval & mask) >> 1; > + rval &= mask; > + vsbits = rval & VS_MODE_INTERRUPTS; > + rval &= ~VS_MODE_INTERRUPTS; > + *ret_val = rval | (vsbits >> 1); > } > > return ret; > @@ -3112,6 +3150,52 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, > return RISCV_EXCP_NONE; > } > > +static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, > + uint64_t *ret_val, > + uint64_t new_val, uint64_t wr_mask) > +{ > + uint64_t mask = wr_mask & hvien_writable_mask; > + > + if (ret_val) { > + *ret_val = env->hvien; > + } > + > + env->hvien = (env->hvien & ~mask) | (new_val & mask); > + > + return RISCV_EXCP_NONE; > +} > + > +static RISCVException rmw_hvien(CPURISCVState *env, int csrno, > + target_ulong *ret_val, > + target_ulong new_val, target_ulong wr_mask) > +{ > + uint64_t rval; > + RISCVException ret; > + > + ret = rmw_hvien64(env, csrno, &rval, new_val, wr_mask); > + if (ret_val) { > + *ret_val = rval; > + } > + > + return ret; > +} > + > +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, > + target_ulong *ret_val, > + target_ulong new_val, target_ulong wr_mask) > +{ > + uint64_t rval; > + RISCVException ret; > + > + ret = rmw_hvien64(env, csrno, &rval, > + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); > + if (ret_val) { > + *ret_val = rval >> 32; > + } > + > + return ret; > +} > + > static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, > uint64_t *ret_val, > uint64_t new_val, uint64_t wr_mask) > @@ -3157,16 +3241,94 @@ static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, > return ret; > } > > +/* > + * The function is written for two use-cases: > + * 1- To access hvip csr as is for HS-mode access. > + * 2- To access vsip as a combination of hvip, and mip for vs-mode. > + * > + * Both report bits 2, 6, 10 and 13:63. > + * vsip needs to be read-only zero when both hideleg[i] and > + * hvien[i] are zero. > + */ > static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, > uint64_t *ret_val, > uint64_t new_val, uint64_t wr_mask) > { > RISCVException ret; > + uint64_t old_hvip; > + uint64_t ret_mip; > + > + /* > + * For bits 10, 6 and 2, vsip[i] is an alias of hip[i]. These bits are > + * present in hip, hvip and mip. Where mip[i] is alias of hip[i] and hvip[i] > + * is OR'ed in hip[i] to inject virtual interrupts from hypervisor. These > + * bits are actually being maintained in mip so we read them from there. > + * This way we have a single source of truth and allows for easier > + * implementation. > + * > + * For bits 13:63 we have: > + * > + * hideleg[i] hvien[i] > + * 0 0 No delegation. vsip[i] readonly zero. > + * 0 1 vsip[i] is alias of hvip[i], sip bypassed. > + * 1 X vsip[i] is alias of sip[i], hvip bypassed. > + * > + * alias_mask denotes the bits that come from sip (mip here given we > + * maintain all bits there). nalias_mask denotes bits that come from > + * hvip. > + */ > + uint64_t alias_mask = (env->hideleg | ~env->hvien) | VS_MODE_INTERRUPTS; > + uint64_t nalias_mask = (~env->hideleg & env->hvien); > + uint64_t wr_mask_hvip; > + uint64_t wr_mask_mip; > + > + /* > + * Both alias and non-alias mask remain same for vsip except: > + * 1- For VS* bits if they are zero in hideleg. > + * 2- For 13:63 bits if they are zero in both hideleg and hvien. > + */ > + if (csrno == CSR_VSIP) { > + /* zero-out VS* bits that are not delegated to VS mode. */ > + alias_mask &= (env->hideleg | ~VS_MODE_INTERRUPTS); > + > + /* > + * zero-out 13:63 bits that are zero in both hideleg and hvien. > + * nalias_mask mask can not contain any VS* bits so only second > + * condition applies on it. > + */ > + nalias_mask &= (env->hideleg | env->hvien); > + alias_mask &= (env->hideleg | env->hvien); > + } > + > + wr_mask_hvip = wr_mask & nalias_mask & hvip_writable_mask; > + wr_mask_mip = wr_mask & alias_mask & hvip_writable_mask; > + > + /* Aliased bits, bits 10, 6, 2 need to come from mip. */ > + ret = rmw_mip64(env, csrno, &ret_mip, new_val, wr_mask_mip); > + if (ret != RISCV_EXCP_NONE) { > + return ret; > + } > + > + old_hvip = env->hvip; > + > + if (wr_mask_hvip) { > + env->hvip = (env->hvip & ~wr_mask_hvip) | (new_val & wr_mask_hvip); > + > + /* > + * Given hvip is separate source from mip, we need to trigger interrupt > + * from here separately. Normally this happen from riscv_cpu_update_mip. > + */ > + riscv_cpu_interrupt(env); > + } > > - ret = rmw_mip64(env, csrno, ret_val, new_val, > - wr_mask & hvip_writable_mask); > if (ret_val) { > - *ret_val &= VS_MODE_INTERRUPTS; > + /* Only take VS* bits from mip. */ > + ret_mip &= alias_mask; > + > + /* Take in non-delegated 13:63 bits from hvip. */ > + old_hvip &= nalias_mask; > + > + *ret_val = ret_mip | old_hvip; > } > > return ret; > @@ -4527,14 +4689,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > .min_priv_ver = PRIV_VERSION_1_12_0 }, > > /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ > - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, > + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, > [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, > write_hvictl }, > [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, > write_hviprio1 }, > [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, > write_hviprio2 }, > - > /* > * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) > */ > @@ -4549,8 +4710,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ > [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, > rmw_hidelegh }, > - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, > - write_ignore }, > + [CSR_HVIENH] = { "hvienh", aia_hmode32, NULL, NULL, rmw_hvienh }, > [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, > [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, > write_hviprio1h }, > diff --git a/target/riscv/machine.c b/target/riscv/machine.c > index dd7bdbb691..3fff230a1c 100644 > --- a/target/riscv/machine.c > +++ b/target/riscv/machine.c > @@ -92,6 +92,8 @@ static const VMStateDescription vmstate_hyper = { > VMSTATE_UINTTL(env.hgatp, RISCVCPU), > VMSTATE_UINTTL(env.hgeie, RISCVCPU), > VMSTATE_UINTTL(env.hgeip, RISCVCPU), > + VMSTATE_UINT64(env.hvien, RISCVCPU), > + VMSTATE_UINT64(env.hvip, RISCVCPU), > VMSTATE_UINT64(env.htimedelta, RISCVCPU), > VMSTATE_UINT64(env.vstimecmp, RISCVCPU), > > @@ -106,6 +108,7 @@ static const VMStateDescription vmstate_hyper = { > VMSTATE_UINTTL(env.vstval, RISCVCPU), > VMSTATE_UINTTL(env.vsatp, RISCVCPU), > VMSTATE_UINTTL(env.vsiselect, RISCVCPU), > + VMSTATE_UINT64(env.vsie, RISCVCPU), > > VMSTATE_UINTTL(env.mtval2, RISCVCPU), > VMSTATE_UINTTL(env.mtinst, RISCVCPU),
On Mon, May 22, 2023 at 6:18 PM Daniel Henrique Barboza <dbarboza@ventanamicro.com> wrote: > > > > On 5/18/23 08:38, Rajnesh Kanwal wrote: > > This change adds support for inserting virtual interrupts from HS-mode > > into VS-mode using hvien and hvip csrs. This also allows for IRQ filtering > > from HS-mode. > > > > Also, the spec doesn't mandate the interrupt to be actually supported > > in hardware. Which allows HS-mode to assert virtual interrupts to VS-mode > > that have no connection to any real interrupt events. > > > > This is defined as part of the AIA specification [0], "6.3.2 Virtual > > interrupts for VS level". > > > > [0]: https://github.com/riscv/riscv-aia/releases/download/1.0-RC4/riscv-interrupts-1.0-RC4.pdf > > > > Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> > > --- > > target/riscv/cpu.c | 3 +- > > target/riscv/cpu.h | 14 +++ > > target/riscv/cpu_helper.c | 48 +++++++--- > > target/riscv/csr.c | 196 ++++++++++++++++++++++++++++++++++---- > > target/riscv/machine.c | 3 + > > 5 files changed, 234 insertions(+), 30 deletions(-) > > > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > > index 9557194a21..c2b05d4c37 100644 > > --- a/target/riscv/cpu.c > > +++ b/target/riscv/cpu.c > > @@ -713,7 +713,8 @@ static bool riscv_cpu_has_work(CPUState *cs) > > * mode and delegation registers, but respect individual enables > > */ > > return riscv_cpu_all_pending(env) != 0 || > > - riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE; > > + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || > > + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; > > #else > > return true; > > #endif > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > > index 07cf656471..3e10eee38f 100644 > > --- a/target/riscv/cpu.h > > +++ b/target/riscv/cpu.h > > @@ -196,6 +196,12 @@ struct CPUArchState { > > */ > > uint64_t sie; > > > > + /* > > + * When hideleg[i]=0 and hvien[i]=1, vsie[i] is no more > > + * alias of sie[i] (mie[i]) and needs to be maintained separatly. > > + */ > > + uint64_t vsie; > > + > > target_ulong satp; /* since: priv-1.10.0 */ > > target_ulong stval; > > target_ulong medeleg; > > @@ -230,6 +236,14 @@ struct CPUArchState { > > target_ulong hgeie; > > target_ulong hgeip; > > uint64_t htimedelta; > > + uint64_t hvien; > > + > > + /* > > + * Bits VSSIP, VSTIP and VSEIP in hvip are maintained in mip. Other bits > > + * from 0:12 are reserved. Bits 13:63 are not aliased and must be separately > > + * maintain in hvip. > > + */ > > + uint64_t hvip; > > > > /* Hypervisor controlled virtual interrupt priorities */ > > target_ulong hvictl; > > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c > > index 681b4ae811..80bdd4cf5a 100644 > > --- a/target/riscv/cpu_helper.c > > +++ b/target/riscv/cpu_helper.c > > @@ -366,8 +366,9 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, > > } > > > > /* > > - * Doesn't report interrupts inserted using mvip from M-mode firmware. Those > > - * are returned in riscv_cpu_sirq_pending(). > > + * Doesn't report interrupts inserted using mvip from M-mode firmware or > > + * using hvip bits 13:63 from HS-mode. Those are returned in > > + * riscv_cpu_sirq_pending() and riscv_cpu_vsirq_pending(). > > */ > > uint64_t riscv_cpu_all_pending(CPURISCVState *env) > > { > > @@ -399,16 +400,23 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) > > > > int riscv_cpu_vsirq_pending(CPURISCVState *env) > > { > > - uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & > > - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); > > + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & env->hideleg; > > + uint64_t irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; > > + uint64_t vsbits; > > + > > + /* Bring VS-level bits to correct position */ > > + vsbits = irqs & VS_MODE_INTERRUPTS; > > + irqs &= ~VS_MODE_INTERRUPTS; > > + irqs |= vsbits >> 1; > > > > return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, > > - irqs >> 1, env->hviprio); > > + (irqs | irqs_f_vs), env->hviprio); > > } > > > > static int riscv_cpu_local_irq_pending(CPURISCVState *env) > > { > > - uint64_t irqs, pending, mie, hsie, vsie, irqs_f; > > + uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; > > + uint64_t vsbits, irq_delegated; > > int virq; > > > > /* Determine interrupt enable state of all privilege modes */ > > @@ -445,12 +453,26 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) > > irqs, env->siprio); > > } > > > > + /* Check for virtual VS-mode interrupts. */ > > + irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; > > + > > /* Check VS-mode interrupts */ > > - irqs = pending & env->mideleg & env->hideleg & -vsie; > > + irq_delegated = pending & env->mideleg & env->hideleg; > > + > > + /* Bring VS-level bits to correct position */ > > + vsbits = irq_delegated & VS_MODE_INTERRUPTS; > > + irq_delegated &= ~VS_MODE_INTERRUPTS; > > + irq_delegated |= vsbits >> 1; > > + > > + irqs = (irq_delegated | irqs_f_vs) & -vsie; > > if (irqs) { > > virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, > > - irqs >> 1, env->hviprio); > > - return (virq <= 0) ? virq : virq + 1; > > + irqs, env->hviprio); > > + if (virq <= 0 || (virq > 12 && virq <= 63)) { > > + return virq; > > + } else { > > + return virq + 1; > > + } > > } > > > > /* Indicate no pending interrupt */ > > @@ -625,6 +647,7 @@ void riscv_cpu_interrupt(CPURISCVState *env) > > if (env->virt_enabled) { > > gein = get_field(env->hstatus, HSTATUS_VGEIN); > > vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; > > + irqf = env->hvien & env->hvip & env->vsie; > > } else { > > irqf = env->mvien & env->mvip & env->sie; > > } > > @@ -1620,6 +1643,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) > > uint64_t deleg = async ? env->mideleg : env->medeleg; > > bool s_injected = env->mvip & (1 << cause) & env->mvien && > > !(env->mip & (1 << cause)); > > + bool vs_injected = env->hvip & (1 << cause) & env->hvien && > > + !(env->mip & (1 << cause)); > > target_ulong tval = 0; > > target_ulong tinst = 0; > > target_ulong htval = 0; > > @@ -1711,12 +1736,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) > > riscv_cpu_get_trap_name(cause, async)); > > > > if (env->priv <= PRV_S && cause < 64 && > > - (((deleg >> cause) & 1) || s_injected)) { > > + (((deleg >> cause) & 1) || s_injected || vs_injected)) { > > /* handle the trap in S-mode */ > > if (riscv_has_ext(env, RVH)) { > > uint64_t hdeleg = async ? env->hideleg : env->hedeleg; > > > > - if (env->virt_enabled && ((hdeleg >> cause) & 1)) { > > + if (env->virt_enabled && > > + (((hdeleg >> cause) & 1) || vs_injected)) { > > /* Trap to VS mode */ > > /* > > * See if we need to adjust cause. Yes if its VS mode interrupt > > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > > index c1ca065a81..1929d5fa7b 100644 > > --- a/target/riscv/csr.c > > +++ b/target/riscv/csr.c > > @@ -30,6 +30,11 @@ > > #include "qemu/guest-random.h" > > #include "qapi/error.h" > > > > + > > +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, > > + uint64_t *ret_val, > > + uint64_t new_val, uint64_t wr_mask); > > + > > This forward declaration breaks qemu linux-user build: > > > [2/26] Compiling C object libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o > FAILED: libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o > cc -m64 -mcx16 -Ilibqemu-riscv32-linux-user.fa.p -I. -I.. -Itarget/riscv -I../target/riscv -I../common-user/host/x86_64 -I../linux-user/include/host/x86_64 -I../linux-user/include -Ilinux-user -I../linux-user -I../linux-user/riscv -Iqapi -Itrace -Iui -Iui/shader -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/sysprof-4 -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O2 -g -isystem /home/danielhb/work/qemu/linux-headers -isystem linux-headers -iquote . -iquote /home/danielhb/work/qemu -iquote /home/danielhb/work/qemu/include -iquote /home/danielhb/work/qemu/tcg/i386 -pthread -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -Wundef -Wwrite-strings -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wold-style-declaration -Wold-style-definition -Wtype-limits -Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers -Wempty-body -Wnested-externs -Wendif-labels -Wexpansion-to-defined -Wimplicit-fallthrough=2 -Wmissing-format-attribute -Wno-missing-include-dirs -Wno-shift-negative-value -Wno-psabi -fstack-protector-strong -fPIE -isystem../linux-headers -isystemlinux-headers -DNEED_CPU_H '-DCONFIG_TARGET="riscv32-linux-user-config-target.h"' '-DCONFIG_DEVICES="riscv32-linux-user-config-devices.h"' -MD -MQ libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o -MF libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o.d -o libqemu-riscv32-linux-user.fa.p/target_riscv_csr.c.o -c ../target/riscv/csr.c > ../target/riscv/csr.c:34:23: error: ‘rmw_hvip64’ declared ‘static’ but never defined [-Werror=unused-function] > 34 | static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, > | ^~~~~~~~~~ > cc1: all warnings being treated as errors > > > > It's not clear in the code but rmw_vsip64() is inside a huge "#if defined(CONFIG_USER_ONLY)" > block that starts at line 775. > > Putting "rmw_hvip64" forward declaration right before rmw_vsip64() is enough to fix it. > > > Thanks, > > > Daniel Thanks Daniel. I have fixed both issues related to linux-user build failure in v2. https://lore.kernel.org/all/20230526162308.22892-1-rkanwal@rivosinc.com/ -Rajnesh > > > > /* CSR function table public API */ > > void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) > > { > > @@ -1176,6 +1181,8 @@ static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; > > static const target_ulong hip_writable_mask = MIP_VSSIP; > > static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | > > MIP_VSEIP | LOCAL_INTERRUPTS; > > +static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; > > + > > static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; > > > > const bool valid_vm_1_10_32[16] = { > > @@ -2584,16 +2591,36 @@ static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, > > uint64_t *ret_val, > > uint64_t new_val, uint64_t wr_mask) > > { > > + uint64_t alias_mask = (LOCAL_INTERRUPTS | VS_MODE_INTERRUPTS) & > > + env->hideleg; > > + uint64_t nalias_mask = LOCAL_INTERRUPTS & (~env->hideleg & env->hvien); > > + uint64_t rval, rval_vs, vsbits; > > + uint64_t wr_mask_vsie; > > + uint64_t wr_mask_mie; > > RISCVException ret; > > - uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; > > > > /* Bring VS-level bits to correct position */ > > - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; > > - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; > > + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); > > + new_val &= ~(VS_MODE_INTERRUPTS >> 1); > > + new_val |= vsbits << 1; > > + > > + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); > > + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); > > + wr_mask |= vsbits << 1; > > + > > + wr_mask_mie = wr_mask & alias_mask; > > + wr_mask_vsie = wr_mask & nalias_mask; > > + > > + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask_mie); > > + > > + rval_vs = env->vsie & nalias_mask; > > + env->vsie = (env->vsie & ~wr_mask_vsie) | (new_val & wr_mask_vsie); > > > > - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); > > if (ret_val) { > > - *ret_val = (rval & mask) >> 1; > > + rval &= alias_mask; > > + vsbits = rval & VS_MODE_INTERRUPTS; > > + rval &= ~VS_MODE_INTERRUPTS; > > + *ret_val = rval | (vsbits >> 1) | rval_vs; > > } > > > > return ret; > > @@ -2812,15 +2839,26 @@ static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, > > { > > RISCVException ret; > > uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; > > + uint64_t vsbits; > > > > - /* Bring VS-level bits to correct position */ > > - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; > > - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; > > + /* Add virtualized bits into vsip mask. */ > > + mask |= env->hvien & ~env->hideleg; > > > > - ret = rmw_mip64(env, csrno, &rval, new_val, > > - wr_mask & mask & vsip_writable_mask); > > + /* Bring VS-level bits to correct position */ > > + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); > > + new_val &= ~(VS_MODE_INTERRUPTS >> 1); > > + new_val |= vsbits << 1; > > + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); > > + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); > > + wr_mask |= vsbits << 1; > > + > > + ret = rmw_hvip64(env, csrno, &rval, new_val, > > + wr_mask & mask & vsip_writable_mask); > > if (ret_val) { > > - *ret_val = (rval & mask) >> 1; > > + rval &= mask; > > + vsbits = rval & VS_MODE_INTERRUPTS; > > + rval &= ~VS_MODE_INTERRUPTS; > > + *ret_val = rval | (vsbits >> 1); > > } > > > > return ret; > > @@ -3112,6 +3150,52 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, > > return RISCV_EXCP_NONE; > > } > > > > +static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, > > + uint64_t *ret_val, > > + uint64_t new_val, uint64_t wr_mask) > > +{ > > + uint64_t mask = wr_mask & hvien_writable_mask; > > + > > + if (ret_val) { > > + *ret_val = env->hvien; > > + } > > + > > + env->hvien = (env->hvien & ~mask) | (new_val & mask); > > + > > + return RISCV_EXCP_NONE; > > +} > > + > > +static RISCVException rmw_hvien(CPURISCVState *env, int csrno, > > + target_ulong *ret_val, > > + target_ulong new_val, target_ulong wr_mask) > > +{ > > + uint64_t rval; > > + RISCVException ret; > > + > > + ret = rmw_hvien64(env, csrno, &rval, new_val, wr_mask); > > + if (ret_val) { > > + *ret_val = rval; > > + } > > + > > + return ret; > > +} > > + > > +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, > > + target_ulong *ret_val, > > + target_ulong new_val, target_ulong wr_mask) > > +{ > > + uint64_t rval; > > + RISCVException ret; > > + > > + ret = rmw_hvien64(env, csrno, &rval, > > + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); > > + if (ret_val) { > > + *ret_val = rval >> 32; > > + } > > + > > + return ret; > > +} > > + > > static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, > > uint64_t *ret_val, > > uint64_t new_val, uint64_t wr_mask) > > @@ -3157,16 +3241,94 @@ static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, > > return ret; > > } > > > > +/* > > + * The function is written for two use-cases: > > + * 1- To access hvip csr as is for HS-mode access. > > + * 2- To access vsip as a combination of hvip, and mip for vs-mode. > > + * > > + * Both report bits 2, 6, 10 and 13:63. > > + * vsip needs to be read-only zero when both hideleg[i] and > > + * hvien[i] are zero. > > + */ > > static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, > > uint64_t *ret_val, > > uint64_t new_val, uint64_t wr_mask) > > { > > RISCVException ret; > > + uint64_t old_hvip; > > + uint64_t ret_mip; > > + > > + /* > > + * For bits 10, 6 and 2, vsip[i] is an alias of hip[i]. These bits are > > + * present in hip, hvip and mip. Where mip[i] is alias of hip[i] and hvip[i] > > + * is OR'ed in hip[i] to inject virtual interrupts from hypervisor. These > > + * bits are actually being maintained in mip so we read them from there. > > + * This way we have a single source of truth and allows for easier > > + * implementation. > > + * > > + * For bits 13:63 we have: > > + * > > + * hideleg[i] hvien[i] > > + * 0 0 No delegation. vsip[i] readonly zero. > > + * 0 1 vsip[i] is alias of hvip[i], sip bypassed. > > + * 1 X vsip[i] is alias of sip[i], hvip bypassed. > > + * > > + * alias_mask denotes the bits that come from sip (mip here given we > > + * maintain all bits there). nalias_mask denotes bits that come from > > + * hvip. > > + */ > > + uint64_t alias_mask = (env->hideleg | ~env->hvien) | VS_MODE_INTERRUPTS; > > + uint64_t nalias_mask = (~env->hideleg & env->hvien); > > + uint64_t wr_mask_hvip; > > + uint64_t wr_mask_mip; > > + > > + /* > > + * Both alias and non-alias mask remain same for vsip except: > > + * 1- For VS* bits if they are zero in hideleg. > > + * 2- For 13:63 bits if they are zero in both hideleg and hvien. > > + */ > > + if (csrno == CSR_VSIP) { > > + /* zero-out VS* bits that are not delegated to VS mode. */ > > + alias_mask &= (env->hideleg | ~VS_MODE_INTERRUPTS); > > + > > + /* > > + * zero-out 13:63 bits that are zero in both hideleg and hvien. > > + * nalias_mask mask can not contain any VS* bits so only second > > + * condition applies on it. > > + */ > > + nalias_mask &= (env->hideleg | env->hvien); > > + alias_mask &= (env->hideleg | env->hvien); > > + } > > + > > + wr_mask_hvip = wr_mask & nalias_mask & hvip_writable_mask; > > + wr_mask_mip = wr_mask & alias_mask & hvip_writable_mask; > > + > > + /* Aliased bits, bits 10, 6, 2 need to come from mip. */ > > + ret = rmw_mip64(env, csrno, &ret_mip, new_val, wr_mask_mip); > > + if (ret != RISCV_EXCP_NONE) { > > + return ret; > > + } > > + > > + old_hvip = env->hvip; > > + > > + if (wr_mask_hvip) { > > + env->hvip = (env->hvip & ~wr_mask_hvip) | (new_val & wr_mask_hvip); > > + > > + /* > > + * Given hvip is separate source from mip, we need to trigger interrupt > > + * from here separately. Normally this happen from riscv_cpu_update_mip. > > + */ > > + riscv_cpu_interrupt(env); > > + } > > > > - ret = rmw_mip64(env, csrno, ret_val, new_val, > > - wr_mask & hvip_writable_mask); > > if (ret_val) { > > - *ret_val &= VS_MODE_INTERRUPTS; > > + /* Only take VS* bits from mip. */ > > + ret_mip &= alias_mask; > > + > > + /* Take in non-delegated 13:63 bits from hvip. */ > > + old_hvip &= nalias_mask; > > + > > + *ret_val = ret_mip | old_hvip; > > } > > > > return ret; > > @@ -4527,14 +4689,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > > .min_priv_ver = PRIV_VERSION_1_12_0 }, > > > > /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ > > - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, > > + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, > > [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, > > write_hvictl }, > > [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, > > write_hviprio1 }, > > [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, > > write_hviprio2 }, > > - > > /* > > * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) > > */ > > @@ -4549,8 +4710,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > > /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ > > [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, > > rmw_hidelegh }, > > - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, > > - write_ignore }, > > + [CSR_HVIENH] = { "hvienh", aia_hmode32, NULL, NULL, rmw_hvienh }, > > [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, > > [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, > > write_hviprio1h }, > > diff --git a/target/riscv/machine.c b/target/riscv/machine.c > > index dd7bdbb691..3fff230a1c 100644 > > --- a/target/riscv/machine.c > > +++ b/target/riscv/machine.c > > @@ -92,6 +92,8 @@ static const VMStateDescription vmstate_hyper = { > > VMSTATE_UINTTL(env.hgatp, RISCVCPU), > > VMSTATE_UINTTL(env.hgeie, RISCVCPU), > > VMSTATE_UINTTL(env.hgeip, RISCVCPU), > > + VMSTATE_UINT64(env.hvien, RISCVCPU), > > + VMSTATE_UINT64(env.hvip, RISCVCPU), > > VMSTATE_UINT64(env.htimedelta, RISCVCPU), > > VMSTATE_UINT64(env.vstimecmp, RISCVCPU), > > > > @@ -106,6 +108,7 @@ static const VMStateDescription vmstate_hyper = { > > VMSTATE_UINTTL(env.vstval, RISCVCPU), > > VMSTATE_UINTTL(env.vsatp, RISCVCPU), > > VMSTATE_UINTTL(env.vsiselect, RISCVCPU), > > + VMSTATE_UINT64(env.vsie, RISCVCPU), > > > > VMSTATE_UINTTL(env.mtval2, RISCVCPU), > > VMSTATE_UINTTL(env.mtinst, RISCVCPU),
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9557194a21..c2b05d4c37 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -713,7 +713,8 @@ static bool riscv_cpu_has_work(CPUState *cs) * mode and delegation registers, but respect individual enables */ return riscv_cpu_all_pending(env) != 0 || - riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE; + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; #else return true; #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 07cf656471..3e10eee38f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -196,6 +196,12 @@ struct CPUArchState { */ uint64_t sie; + /* + * When hideleg[i]=0 and hvien[i]=1, vsie[i] is no more + * alias of sie[i] (mie[i]) and needs to be maintained separatly. + */ + uint64_t vsie; + target_ulong satp; /* since: priv-1.10.0 */ target_ulong stval; target_ulong medeleg; @@ -230,6 +236,14 @@ struct CPUArchState { target_ulong hgeie; target_ulong hgeip; uint64_t htimedelta; + uint64_t hvien; + + /* + * Bits VSSIP, VSTIP and VSEIP in hvip are maintained in mip. Other bits + * from 0:12 are reserved. Bits 13:63 are not aliased and must be separately + * maintain in hvip. + */ + uint64_t hvip; /* Hypervisor controlled virtual interrupt priorities */ target_ulong hvictl; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 681b4ae811..80bdd4cf5a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -366,8 +366,9 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, } /* - * Doesn't report interrupts inserted using mvip from M-mode firmware. Those - * are returned in riscv_cpu_sirq_pending(). + * Doesn't report interrupts inserted using mvip from M-mode firmware or + * using hvip bits 13:63 from HS-mode. Those are returned in + * riscv_cpu_sirq_pending() and riscv_cpu_vsirq_pending(). */ uint64_t riscv_cpu_all_pending(CPURISCVState *env) { @@ -399,16 +400,23 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) int riscv_cpu_vsirq_pending(CPURISCVState *env) { - uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & env->hideleg; + uint64_t irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + uint64_t vsbits; + + /* Bring VS-level bits to correct position */ + vsbits = irqs & VS_MODE_INTERRUPTS; + irqs &= ~VS_MODE_INTERRUPTS; + irqs |= vsbits >> 1; return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); + (irqs | irqs_f_vs), env->hviprio); } static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - uint64_t irqs, pending, mie, hsie, vsie, irqs_f; + uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; + uint64_t vsbits, irq_delegated; int virq; /* Determine interrupt enable state of all privilege modes */ @@ -445,12 +453,26 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) irqs, env->siprio); } + /* Check for virtual VS-mode interrupts. */ + irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + /* Check VS-mode interrupts */ - irqs = pending & env->mideleg & env->hideleg & -vsie; + irq_delegated = pending & env->mideleg & env->hideleg; + + /* Bring VS-level bits to correct position */ + vsbits = irq_delegated & VS_MODE_INTERRUPTS; + irq_delegated &= ~VS_MODE_INTERRUPTS; + irq_delegated |= vsbits >> 1; + + irqs = (irq_delegated | irqs_f_vs) & -vsie; if (irqs) { virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); - return (virq <= 0) ? virq : virq + 1; + irqs, env->hviprio); + if (virq <= 0 || (virq > 12 && virq <= 63)) { + return virq; + } else { + return virq + 1; + } } /* Indicate no pending interrupt */ @@ -625,6 +647,7 @@ void riscv_cpu_interrupt(CPURISCVState *env) if (env->virt_enabled) { gein = get_field(env->hstatus, HSTATUS_VGEIN); vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + irqf = env->hvien & env->hvip & env->vsie; } else { irqf = env->mvien & env->mvip & env->sie; } @@ -1620,6 +1643,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) uint64_t deleg = async ? env->mideleg : env->medeleg; bool s_injected = env->mvip & (1 << cause) & env->mvien && !(env->mip & (1 << cause)); + bool vs_injected = env->hvip & (1 << cause) & env->hvien && + !(env->mip & (1 << cause)); target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; @@ -1711,12 +1736,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) riscv_cpu_get_trap_name(cause, async)); if (env->priv <= PRV_S && cause < 64 && - (((deleg >> cause) & 1) || s_injected)) { + (((deleg >> cause) & 1) || s_injected || vs_injected)) { /* handle the trap in S-mode */ if (riscv_has_ext(env, RVH)) { uint64_t hdeleg = async ? env->hideleg : env->hedeleg; - if (env->virt_enabled && ((hdeleg >> cause) & 1)) { + if (env->virt_enabled && + (((hdeleg >> cause) & 1) || vs_injected)) { /* Trap to VS mode */ /* * See if we need to adjust cause. Yes if its VS mode interrupt diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c1ca065a81..1929d5fa7b 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -30,6 +30,11 @@ #include "qemu/guest-random.h" #include "qapi/error.h" + +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask); + /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) { @@ -1176,6 +1181,8 @@ static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; static const target_ulong hip_writable_mask = MIP_VSSIP; static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | LOCAL_INTERRUPTS; +static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; + static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; const bool valid_vm_1_10_32[16] = { @@ -2584,16 +2591,36 @@ static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { + uint64_t alias_mask = (LOCAL_INTERRUPTS | VS_MODE_INTERRUPTS) & + env->hideleg; + uint64_t nalias_mask = LOCAL_INTERRUPTS & (~env->hideleg & env->hvien); + uint64_t rval, rval_vs, vsbits; + uint64_t wr_mask_vsie; + uint64_t wr_mask_mie; RISCVException ret; - uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; + + wr_mask_mie = wr_mask & alias_mask; + wr_mask_vsie = wr_mask & nalias_mask; + + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask_mie); + + rval_vs = env->vsie & nalias_mask; + env->vsie = (env->vsie & ~wr_mask_vsie) | (new_val & wr_mask_vsie); - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= alias_mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1) | rval_vs; } return ret; @@ -2812,15 +2839,26 @@ static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, { RISCVException ret; uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; + uint64_t vsbits; - /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + /* Add virtualized bits into vsip mask. */ + mask |= env->hvien & ~env->hideleg; - ret = rmw_mip64(env, csrno, &rval, new_val, - wr_mask & mask & vsip_writable_mask); + /* Bring VS-level bits to correct position */ + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; + + ret = rmw_hvip64(env, csrno, &rval, new_val, + wr_mask & mask & vsip_writable_mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1); } return ret; @@ -3112,6 +3150,52 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & hvien_writable_mask; + + if (ret_val) { + *ret_val = env->hvien; + } + + env->hvien = (env->hvien & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_hvien(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) @@ -3157,16 +3241,94 @@ static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, return ret; } +/* + * The function is written for two use-cases: + * 1- To access hvip csr as is for HS-mode access. + * 2- To access vsip as a combination of hvip, and mip for vs-mode. + * + * Both report bits 2, 6, 10 and 13:63. + * vsip needs to be read-only zero when both hideleg[i] and + * hvien[i] are zero. + */ static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; + uint64_t old_hvip; + uint64_t ret_mip; + + /* + * For bits 10, 6 and 2, vsip[i] is an alias of hip[i]. These bits are + * present in hip, hvip and mip. Where mip[i] is alias of hip[i] and hvip[i] + * is OR'ed in hip[i] to inject virtual interrupts from hypervisor. These + * bits are actually being maintained in mip so we read them from there. + * This way we have a single source of truth and allows for easier + * implementation. + * + * For bits 13:63 we have: + * + * hideleg[i] hvien[i] + * 0 0 No delegation. vsip[i] readonly zero. + * 0 1 vsip[i] is alias of hvip[i], sip bypassed. + * 1 X vsip[i] is alias of sip[i], hvip bypassed. + * + * alias_mask denotes the bits that come from sip (mip here given we + * maintain all bits there). nalias_mask denotes bits that come from + * hvip. + */ + uint64_t alias_mask = (env->hideleg | ~env->hvien) | VS_MODE_INTERRUPTS; + uint64_t nalias_mask = (~env->hideleg & env->hvien); + uint64_t wr_mask_hvip; + uint64_t wr_mask_mip; + + /* + * Both alias and non-alias mask remain same for vsip except: + * 1- For VS* bits if they are zero in hideleg. + * 2- For 13:63 bits if they are zero in both hideleg and hvien. + */ + if (csrno == CSR_VSIP) { + /* zero-out VS* bits that are not delegated to VS mode. */ + alias_mask &= (env->hideleg | ~VS_MODE_INTERRUPTS); + + /* + * zero-out 13:63 bits that are zero in both hideleg and hvien. + * nalias_mask mask can not contain any VS* bits so only second + * condition applies on it. + */ + nalias_mask &= (env->hideleg | env->hvien); + alias_mask &= (env->hideleg | env->hvien); + } + + wr_mask_hvip = wr_mask & nalias_mask & hvip_writable_mask; + wr_mask_mip = wr_mask & alias_mask & hvip_writable_mask; + + /* Aliased bits, bits 10, 6, 2 need to come from mip. */ + ret = rmw_mip64(env, csrno, &ret_mip, new_val, wr_mask_mip); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + old_hvip = env->hvip; + + if (wr_mask_hvip) { + env->hvip = (env->hvip & ~wr_mask_hvip) | (new_val & wr_mask_hvip); + + /* + * Given hvip is separate source from mip, we need to trigger interrupt + * from here separately. Normally this happen from riscv_cpu_update_mip. + */ + riscv_cpu_interrupt(env); + } - ret = rmw_mip64(env, csrno, ret_val, new_val, - wr_mask & hvip_writable_mask); if (ret_val) { - *ret_val &= VS_MODE_INTERRUPTS; + /* Only take VS* bits from mip. */ + ret_mip &= alias_mask; + + /* Take in non-delegated 13:63 bits from hvip. */ + old_hvip &= nalias_mask; + + *ret_val = ret_mip | old_hvip; } return ret; @@ -4527,14 +4689,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, - /* * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ @@ -4549,8 +4710,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, - write_ignore }, + [CSR_HVIENH] = { "hvienh", aia_hmode32, NULL, NULL, rmw_hvienh }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index dd7bdbb691..3fff230a1c 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -92,6 +92,8 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.hgatp, RISCVCPU), VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), + VMSTATE_UINT64(env.hvien, RISCVCPU), + VMSTATE_UINT64(env.hvip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), VMSTATE_UINT64(env.vstimecmp, RISCVCPU), @@ -106,6 +108,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.vstval, RISCVCPU), VMSTATE_UINTTL(env.vsatp, RISCVCPU), VMSTATE_UINTTL(env.vsiselect, RISCVCPU), + VMSTATE_UINT64(env.vsie, RISCVCPU), VMSTATE_UINTTL(env.mtval2, RISCVCPU), VMSTATE_UINTTL(env.mtinst, RISCVCPU),
This change adds support for inserting virtual interrupts from HS-mode into VS-mode using hvien and hvip csrs. This also allows for IRQ filtering from HS-mode. Also, the spec doesn't mandate the interrupt to be actually supported in hardware. Which allows HS-mode to assert virtual interrupts to VS-mode that have no connection to any real interrupt events. This is defined as part of the AIA specification [0], "6.3.2 Virtual interrupts for VS level". [0]: https://github.com/riscv/riscv-aia/releases/download/1.0-RC4/riscv-interrupts-1.0-RC4.pdf Signed-off-by: Rajnesh Kanwal <rkanwal@rivosinc.com> --- target/riscv/cpu.c | 3 +- target/riscv/cpu.h | 14 +++ target/riscv/cpu_helper.c | 48 +++++++--- target/riscv/csr.c | 196 ++++++++++++++++++++++++++++++++++---- target/riscv/machine.c | 3 + 5 files changed, 234 insertions(+), 30 deletions(-)