On Tue, Aug 20, 2024 at 2:14 AM Ian Brockbank <ian.brockb...@cirrus.com> wrote: > > From: Ian Brockbank <ian.brockb...@cirrus.com> > > Decode CLIC interrupt information from exccode, includes interrupt > privilege mode, interrupt level, and irq number. > > Then update CSRs xcause, xstatus, xepc, xintstatus and jump to > correct PC according to the CLIC specification. > > Signed-off-by: LIU Zhiwei <zhiwei_...@c-sky.com> > Signed-off-by: Ian Brockbank <ian.brockb...@cirrus.com> > --- > target/riscv/cpu_helper.c | 129 +++++++++++++++++++++++++++++++++++--- > 1 file changed, 119 insertions(+), 10 deletions(-) > > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c > index 395a1d9140..944afb68d2 100644 > --- a/target/riscv/cpu_helper.c > +++ b/target/riscv/cpu_helper.c > @@ -24,6 +24,7 @@ > #include "internals.h" > #include "pmu.h" > #include "exec/exec-all.h" > +#include "exec/cpu_ldst.h" > #include "exec/page-protection.h" > #include "instmap.h" > #include "tcg/tcg-op.h" > @@ -33,6 +34,7 @@ > #include "cpu_bits.h" > #include "debug.h" > #include "tcg/oversized-guest.h" > +#include "hw/intc/riscv_clic.h" > > int riscv_env_mmu_index(CPURISCVState *env, bool ifetch) > { > @@ -428,6 +430,20 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env) > (irqs | irqs_f_vs), env->hviprio); > } > > +static int riscv_cpu_local_irq_mode_enabled(CPURISCVState *env, int mode) > +{ > + switch (mode) { > + case PRV_M: > + return env->priv < PRV_M || > + (env->priv == PRV_M && get_field(env->mstatus, MSTATUS_MIE)); > + case PRV_S: > + return env->priv < PRV_S || > + (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_SIE)); > + default: > + return false; > + } > +} > + > static int riscv_cpu_local_irq_pending(CPURISCVState *env) > { > uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; > @@ -506,6 +522,18 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int > interrupt_request) > return true; > } > } > + if (interrupt_request & CPU_INTERRUPT_CLIC) { > + RISCVCPU *cpu = RISCV_CPU(cs); > + CPURISCVState *env = &cpu->env; > + int mode = get_field(env->exccode, RISCV_EXCP_CLIC_MODE); > + int enabled = riscv_cpu_local_irq_mode_enabled(env, mode); > + if (enabled) { > + cs->exception_index = RISCV_EXCP_CLIC | env->exccode; > + cs->interrupt_request = cs->interrupt_request & > ~CPU_INTERRUPT_CLIC; > + riscv_cpu_do_interrupt(cs); > + return true; > + } > + } > return false; > } > > @@ -1641,6 +1669,60 @@ static target_ulong > riscv_transformed_insn(CPURISCVState *env, > return xinsn; > } > > +static target_ulong riscv_intr_pc(CPURISCVState *env, target_ulong tvec, > + target_ulong tvt, bool async, > + int cause, int mode) > +{ > + int mode1 = tvec & XTVEC_MODE; > + int mode2 = tvec & XTVEC_FULL_MODE; > +
This is going to need extension checks > + if (!async) { > + return tvec & XTVEC_OBASE; > + } > + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ > + switch (mode1) { > + case XTVEC_CLINT_DIRECT: > + return tvec & XTVEC_OBASE; > + case XTVEC_CLINT_VECTORED: > + return (tvec & XTVEC_OBASE) + cause * 4; > + default: > + if (env->clic && (mode2 == XTVEC_CLIC)) { > + /* Non-vectored, clicintattr[i].shv = 0 || cliccfg.nvbits = 0 */ > + if (!riscv_clic_shv_interrupt(env->clic, cause)) { > + /* NBASE = mtvec[XLEN-1:6]<<6 */ > + return tvec & XTVEC_NBASE; > + } else { > + /* > + * pc := M[TBASE + XLEN/8 * exccode)] & ~1, > + * TBASE = mtvt[XLEN-1:6]<<6 > + */ > + int size = TARGET_LONG_BITS / 8; > + target_ulong tbase = (tvt & XTVEC_NBASE) + size * cause; > + void *host = tlb_vaddr_to_host(env, tbase, MMU_DATA_LOAD, > mode); This doesn't look right. I think you want cpu_ l*_mmuidx_ra(). That will raise an exception on an access failure > + if (host != NULL) { > + target_ulong new_pc = tbase; > + if (!riscv_clic_use_jump_table(env->clic)) { > + /* > + * Standard CLIC: the vector entry is a function > pointer > + * so look up the destination. > + */ > + new_pc = ldn_p(host, size); > + host = tlb_vaddr_to_host(env, new_pc, > + MMU_INST_FETCH, mode); At xtvt is the base address of a table of pointers, you also then want to call cpu_ l*_mmuidx_ra() a second time and that value should be returned as the next PC > + } > + if (host) { > + return new_pc; > + } > + } > + qemu_log_mask(LOG_GUEST_ERROR, > + "CLIC: load trap handler error!\n"); > + exit(1); Don't allow the guest to exit. Print a guest error and keep going Alistair