From a7d479ee51c5911c93ee931efe4c2413b54c8756 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 5 Jul 2020 17:31:59 -0700 Subject: [PATCH] target/xtensa: implement NMI support When NMI is configured it is taken regardless of INTENABLE SR contents, PS.INTLEVEL or PS.EXCM. It is cleared automatically once it's taken. Add nmi_level to XtensaConfig, puth there NMI level from the overlay or XCHAL_NUM_INTLEVELS + 1 when NMI is not configured. Add NMI mask to INTENABLE SR and limit CINTLEVEL to nmi_level - 1 when determining pending IRQ level in check_interrupt(). Always take and clear pending interrupt at nmi_level in the handle_interrupt(). Signed-off-by: Max Filippov --- hw/xtensa/pic_cpu.c | 6 +++++- target/xtensa/cpu.h | 1 + target/xtensa/exc_helper.c | 23 +++++++++++++++-------- target/xtensa/overlay_tool.h | 6 +++++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index edd53c9241..1d5982a9e4 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -35,9 +35,13 @@ void check_interrupts(CPUXtensaState *env) { CPUState *cs = env_cpu(env); int minlevel = xtensa_get_cintlevel(env); - uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; + uint32_t int_set_enabled = env->sregs[INTSET] & + (env->sregs[INTENABLE] | env->config->inttype_mask[INTTYPE_NMI]); int level; + if (minlevel >= env->config->nmi_level) { + minlevel = env->config->nmi_level - 1; + } for (level = env->config->nlevel; level > minlevel; --level) { if (env->config->level_mask[level] & int_set_enabled) { env->pending_irq_level = level; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 65f0002850..0c96181212 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -433,6 +433,7 @@ struct XtensaConfig { uint32_t exception_vector[EXC_MAX]; unsigned ninterrupt; unsigned nlevel; + unsigned nmi_level; uint32_t interrupt_vector[MAX_NLEVEL + MAX_NNMI + 1]; uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1]; uint32_t inttype_mask[INTTYPE_MAX]; diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c index 601341d13a..58a64e6d62 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -132,11 +132,15 @@ void HELPER(intset)(CPUXtensaState *env, uint32_t v) v & env->config->inttype_mask[INTTYPE_SOFTWARE]); } +static void intclear(CPUXtensaState *env, uint32_t v) +{ + atomic_and(&env->sregs[INTSET], ~v); +} + void HELPER(intclear)(CPUXtensaState *env, uint32_t v) { - atomic_and(&env->sregs[INTSET], - ~(v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | - env->config->inttype_mask[INTTYPE_EDGE]))); + intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | + env->config->inttype_mask[INTTYPE_EDGE])); } static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) @@ -159,11 +163,11 @@ static void handle_interrupt(CPUXtensaState *env) { int level = env->pending_irq_level; - if (level > xtensa_get_cintlevel(env) && - level <= env->config->nlevel && - (env->config->level_mask[level] & - env->sregs[INTSET] & - env->sregs[INTENABLE])) { + if ((level > xtensa_get_cintlevel(env) && + level <= env->config->nlevel && + (env->config->level_mask[level] & + env->sregs[INTSET] & env->sregs[INTENABLE])) || + level == env->config->nmi_level) { CPUState *cs = env_cpu(env); if (level > 1) { @@ -173,6 +177,9 @@ static void handle_interrupt(CPUXtensaState *env) (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; env->pc = relocated_vector(env, env->config->interrupt_vector[level]); + if (level == env->config->nmi_level) { + intclear(env, env->config->inttype_mask[INTTYPE_NMI]); + } } else { env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index a994e69b6e..eb9f08af0b 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -216,6 +216,9 @@ #define XTHAL_INTTYPE_IDMA_ERR INTTYPE_IDMA_ERR #define XTHAL_INTTYPE_GS_ERR INTTYPE_GS_ERR +#ifndef XCHAL_NMILEVEL +#define XCHAL_NMILEVEL (XCHAL_NUM_INTLEVELS + 1) +#endif #define INTERRUPT(i) { \ .level = XCHAL_INT ## i ## _LEVEL, \ @@ -305,7 +308,8 @@ #define INTERRUPTS_SECTION \ .ninterrupt = XCHAL_NUM_INTERRUPTS, \ - .nlevel = XCHAL_NUM_INTLEVELS, \ + .nlevel = XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI, \ + .nmi_level = XCHAL_NMILEVEL, \ .interrupt_vector = INTERRUPT_VECTORS, \ .level_mask = LEVEL_MASKS, \ .inttype_mask = INTTYPE_MASKS, \