Anup Patel 於 2021年12月30日 週四 下午8:45寫道: > From: Anup Patel > > The hgeie and hgeip CSRs are required for emulating an external > interrupt controller capable of injecting virtual external interrupt > to Guest/VM running at VS-level. > > Signed-off-by: Anup Patel > Signed-off-by: Anup Patel > Reviewed-by: Alistair Francis > --- > target/riscv/cpu.c | 61 ++++++++++++++++++++++++++++----------- > target/riscv/cpu.h | 5 ++++ > target/riscv/cpu_bits.h | 1 + > target/riscv/cpu_helper.c | 37 ++++++++++++++++++++++-- > target/riscv/csr.c | 43 ++++++++++++++++++--------- > target/riscv/machine.c | 6 ++-- > 6 files changed, 118 insertions(+), 35 deletions(-) > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > index 7d92ce7555..f4dbc766c2 100644 > --- a/target/riscv/cpu.c > +++ b/target/riscv/cpu.c > @@ -582,23 +582,49 @@ static void riscv_cpu_realize(DeviceState *dev, > Error **errp) > static void riscv_cpu_set_irq(void *opaque, int irq, int level) > { > RISCVCPU *cpu = RISCV_CPU(opaque); > + CPURISCVState *env = &cpu->env; > > - switch (irq) { > - case IRQ_U_SOFT: > - case IRQ_S_SOFT: > - case IRQ_VS_SOFT: > - case IRQ_M_SOFT: > - case IRQ_U_TIMER: > - case IRQ_S_TIMER: > - case IRQ_VS_TIMER: > - case IRQ_M_TIMER: > - case IRQ_U_EXT: > - case IRQ_S_EXT: > - case IRQ_VS_EXT: > - case IRQ_M_EXT: > - riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); > - break; > - default: > + if (irq < IRQ_LOCAL_MAX) { > + switch (irq) { > + case IRQ_U_SOFT: > + case IRQ_S_SOFT: > + case IRQ_VS_SOFT: > + case IRQ_M_SOFT: > + case IRQ_U_TIMER: > + case IRQ_S_TIMER: > + case IRQ_VS_TIMER: > + case IRQ_M_TIMER: > + case IRQ_U_EXT: > + case IRQ_S_EXT: > + case IRQ_VS_EXT: > + case IRQ_M_EXT: > + riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); > + break; > + default: > + g_assert_not_reached(); > + } > + } else if (irq < (IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX)) { > + /* Require H-extension for handling guest local interrupts */ > + if (!riscv_has_ext(env, RVH)) { > + g_assert_not_reached(); > + } > + > + /* Compute bit position in HGEIP CSR */ > + irq = irq - IRQ_LOCAL_MAX + 1; > + if (env->geilen < irq) { > + g_assert_not_reached(); > + } > + > + /* Update HGEIP CSR */ > + env->hgeip &= ~((target_ulong)1 << irq); > + if (level) { > + env->hgeip |= (target_ulong)1 << irq; > + } > + > + /* Update mip.SGEIP bit */ > + riscv_cpu_update_mip(cpu, MIP_SGEIP, > + BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); > + } else { > g_assert_not_reached(); > } > } > @@ -611,7 +637,8 @@ static void riscv_cpu_init(Object *obj) > cpu_set_cpustate_pointers(cpu); > > #ifndef CONFIG_USER_ONLY > - qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, IRQ_LOCAL_MAX); > + qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, > + IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); > #endif /* CONFIG_USER_ONLY */ > } > > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h > index dc10f27093..6895ac138c 100644 > --- a/target/riscv/cpu.h > +++ b/target/riscv/cpu.h > @@ -151,6 +151,7 @@ struct CPURISCVState { > target_ulong priv; > /* This contains QEMU specific information about the virt state. */ > target_ulong virt; > + target_ulong geilen; > target_ulong resetvec; > > target_ulong mhartid; > @@ -188,6 +189,8 @@ struct CPURISCVState { > target_ulong htval; > target_ulong htinst; > target_ulong hgatp; > + target_ulong hgeie; > + target_ulong hgeip; > uint64_t htimedelta; > > /* Virtual CSRs */ > @@ -355,6 +358,8 @@ int riscv_cpu_write_elf32_note(WriteCoreDumpFunction > f, CPUState *cs, > int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); > int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); > bool riscv_cpu_fp_enabled(CPURISCVState *env); > +target_ulong riscv_cpu_get_geilen(CPURISCVState *env); > +void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen); > bool riscv_cpu_vector_enabled(CPURISCVState *env); > bool riscv_cpu_virt_enabled(CPURISCVState *env); > void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h > index fe276d4b34..f32159a19d 100644 > --- a/target/riscv/cpu_bits.h > +++ b/target/riscv/cpu_bits.h > @@ -536,6 +536,7 @@ typedef enum RISCVException { > #define IRQ_M_EXT 11 > #define IRQ_S_GEXT 12 > #define IRQ_LOCAL_MAX 16 > +#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) > > /* mip masks */ > #define MIP_USIP (1 << IRQ_U_SOFT) > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c > index 10f3baba53..bf50699b1f 100644 > --- a/target/riscv/cpu_helper.c > +++ b/target/riscv/cpu_helper.c > @@ -158,7 +158,11 @@ static int riscv_cpu_local_irq_pending(CPURISCVState > *env) > target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); > target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); > > - target_ulong pending = env->mip & env->mie; > + target_ulong vsgemask = > + (target_ulong)1 << get_field(env->hstatus, HSTATUS_VGEIN); > + target_ulong vsgein = (env->hgeip & vsgemask) ? MIP_VSEIP : 0; > + > + target_ulong pending = (env->mip | vsgein) & env->mie; > > target_ulong mie = env->priv < PRV_M || > (env->priv == PRV_M && mstatus_mie); > @@ -278,6 +282,28 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState > *env) > } > } > > +target_ulong riscv_cpu_get_geilen(CPURISCVState *env) > +{ > + if (!riscv_has_ext(env, RVH)) { > + return 0; > + } > + > + return env->geilen; > +} > + > +void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) > +{ > + if (!riscv_has_ext(env, RVH)) { > + return; > + } > + > + if (geilen > (TARGET_LONG_BITS - 1)) { > + return; > + } > + > + env->geilen = geilen; > +} > + > bool riscv_cpu_virt_enabled(CPURISCVState *env) > { > if (!riscv_has_ext(env, RVH)) { > @@ -321,9 +347,14 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t > mask, uint32_t value) > { > CPURISCVState *env = &cpu->env; > CPUState *cs = CPU(cpu); > - uint32_t old = env->mip; > + uint32_t gein, vsgein = 0, old = env->mip; > bool locked = false; > > + if (riscv_cpu_virt_enabled(env)) { > + gein = get_field(env->hstatus, HSTATUS_VGEIN); > + vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; > + } > + > if (!qemu_mutex_iothread_locked()) { > locked = true; > qemu_mutex_lock_iothread(); > @@ -331,7 +362,7 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t > mask, uint32_t value) > > env->mip = (env->mip & ~mask) | (value & mask); > > - if (env->mip) { > + if (env->mip | vsgein) { > cpu_interrupt(cs, CPU_INTERRUPT_HARD); > } else { > cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > index a4028f28e0..50424a8344 100644 > --- a/target/riscv/csr.c > +++ b/target/riscv/csr.c > @@ -832,7 +832,7 @@ static RISCVException rmw_mip(CPURISCVState *env, int > csrno, > RISCVCPU *cpu = env_archcpu(env); > /* Allow software control of delegable interrupts not claimed by > hardware */ > target_ulong mask = write_mask & delegable_ints & ~env->miclaim; > - uint32_t old_mip; > + uint32_t gin, old_mip; > > if (mask) { > old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); > @@ -840,6 +840,11 @@ static RISCVException rmw_mip(CPURISCVState *env, int > csrno, > old_mip = env->mip; > } > > + if (csrno != CSR_HVIP) { > + gin = get_field(env->hstatus, HSTATUS_VGEIN); > + old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : > 0; > + } > + > if (ret_value) { > *ret_value = old_mip; > } > @@ -1002,7 +1007,7 @@ static RISCVException rmw_vsip(CPURISCVState *env, > int csrno, > target_ulong new_value, target_ulong > write_mask) > { > /* Shift the S bits to their VS bit location in mip */ > - int ret = rmw_mip(env, 0, ret_value, new_value << 1, > + int ret = rmw_mip(env, csrno, ret_value, new_value << 1, > (write_mask << 1) & vsip_writable_mask & > env->hideleg); > > if (ret_value) { > @@ -1022,7 +1027,7 @@ static RISCVException rmw_sip(CPURISCVState *env, > int csrno, > if (riscv_cpu_virt_enabled(env)) { > ret = rmw_vsip(env, CSR_VSIP, ret_value, new_value, write_mask); > } else { > - ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value, > + ret = rmw_mip(env, csrno, ret_value, new_value, > write_mask & env->mideleg & sip_writable_mask); > } > > @@ -1141,7 +1146,7 @@ static RISCVException rmw_hvip(CPURISCVState *env, > int csrno, > target_ulong *ret_value, > target_ulong new_value, target_ulong > write_mask) > { > - int ret = rmw_mip(env, 0, ret_value, new_value, > + int ret = rmw_mip(env, csrno, ret_value, new_value, > write_mask & hvip_writable_mask); > > if (ret_value) { > @@ -1154,7 +1159,7 @@ static RISCVException rmw_hip(CPURISCVState *env, > int csrno, > target_ulong *ret_value, > target_ulong new_value, target_ulong > write_mask) > { > - int ret = rmw_mip(env, 0, ret_value, new_value, > + int ret = rmw_mip(env, csrno, ret_value, new_value, > write_mask & hip_writable_mask); > > if (ret_value) { > @@ -1191,15 +1196,27 @@ static RISCVException > write_hcounteren(CPURISCVState *env, int csrno, > return RISCV_EXCP_NONE; > } > > -static RISCVException write_hgeie(CPURISCVState *env, int csrno, > - target_ulong val) > +static RISCVException read_hgeie(CPURISCVState *env, int csrno, > + target_ulong *val) > { > if (val) { > - qemu_log_mask(LOG_UNIMP, "No support for a non-zero GEILEN."); > + *val = env->hgeie; > } > return RISCV_EXCP_NONE; > } > > +static RISCVException write_hgeie(CPURISCVState *env, int csrno, > + target_ulong val) > +{ > + /* Only GEILEN:1 bits implemented and BIT0 is never implemented */ > + val &= ((((target_ulong)1) << env->geilen) - 1) << 1; > + env->hgeie = val; > + /* Update mip.SGEIP bit */ > + riscv_cpu_update_mip(env_archcpu(env), MIP_SGEIP, > + BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); > + return RISCV_EXCP_NONE; > +} > + > static RISCVException read_htval(CPURISCVState *env, int csrno, > target_ulong *val) > { > @@ -1227,11 +1244,11 @@ static RISCVException write_htinst(CPURISCVState > *env, int csrno, > return RISCV_EXCP_NONE; > } > > -static RISCVException write_hgeip(CPURISCVState *env, int csrno, > - target_ulong val) > +static RISCVException read_hgeip(CPURISCVState *env, int csrno, > + target_ulong *val) > { > if (val) { > - qemu_log_mask(LOG_UNIMP, "No support for a non-zero GEILEN."); > + *val = env->hgeip; > } > return RISCV_EXCP_NONE; > } > @@ -1922,10 +1939,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { > [CSR_HIP] = { "hip", hmode, NULL, NULL, > rmw_hip }, > [CSR_HIE] = { "hie", hmode, read_hie, > write_hie }, > [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, > write_hcounteren }, > - [CSR_HGEIE] = { "hgeie", hmode, read_zero, > write_hgeie }, > + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, > write_hgeie }, > [CSR_HTVAL] = { "htval", hmode, read_htval, > write_htval }, > [CSR_HTINST] = { "htinst", hmode, read_htinst, > write_htinst }, > - [CSR_HGEIP] = { "hgeip", hmode, read_zero, > write_hgeip }, > + [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL > }, > [CSR_HGATP] = { "hgatp", hmode, read_hgatp, > write_hgatp }, > [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, > write_htimedelta }, > [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, > write_htimedeltah }, > diff --git a/target/riscv/machine.c b/target/riscv/machine.c > index ad8248ebfd..76dd0d415c 100644 > --- a/target/riscv/machine.c > +++ b/target/riscv/machine.c > @@ -78,8 +78,8 @@ static bool hyper_needed(void *opaque) > > static const VMStateDescription vmstate_hyper = { > .name = "cpu/hyper", > - .version_id = 1, > - .minimum_version_id = 1, > + .version_id = 2, > + .minimum_version_id = 2, > .needed = hyper_needed, > .fields = (VMStateField[]) { > VMSTATE_UINTTL(env.hstatus, RISCVCPU), > @@ -89,6 +89,8 @@ static const VMStateDescription vmstate_hyper = { > VMSTATE_UINTTL(env.htval, RISCVCPU), > VMSTATE_UINTTL(env.htinst, RISCVCPU), > VMSTATE_UINTTL(env.hgatp, RISCVCPU), > + VMSTATE_UINTTL(env.hgeie, RISCVCPU), > + VMSTATE_UINTTL(env.hgeip, RISCVCPU), > VMSTATE_UINT64(env.htimedelta, RISCVCPU), > > VMSTATE_UINT64(env.vsstatus, RISCVCPU), > -- > 2.25.1 > > > Reviewed-by: Frank Chang