[PATCH] target/xtensa: implement NMI support

Max Filippov posted 1 patch 3 years, 9 months ago
Test FreeBSD passed
Test docker-quick@centos7 passed
Test checkpatch passed
Test docker-mingw@fedora passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20200706015539.7956-1-jcmvbkbc@gmail.com
Maintainers: Max Filippov <jcmvbkbc@gmail.com>
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(-)
[PATCH] target/xtensa: implement NMI support
Posted by Max Filippov 3 years, 9 months ago
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 <jcmvbkbc@gmail.com>
---
 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 edd53c9241c5..1d5982a9e424 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 65f00028501a..0c96181212a5 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 601341d13aa0..58a64e6d620e 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 a994e69b6e96..eb9f08af0bf6 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, \
-- 
2.20.1