Interrupts 3 through 5 are routed to guest mode when CCR:GIE and the
matching CCR:VV bit are set.  Enter through GEVB rather than EVB,
record the pre-entry state in GSR and the return address in GELR, and
read the vector ID from the l2vic.

Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/reg_fields_def.h.inc |  7 +++
 target/hexagon/cpu.c                |  5 ++
 target/hexagon/hex_interrupts.c     | 87 ++++++++++++++++++++++++++---
 3 files changed, 92 insertions(+), 7 deletions(-)

diff --git a/target/hexagon/reg_fields_def.h.inc 
b/target/hexagon/reg_fields_def.h.inc
index d2c706d56b5..29497fbcc4d 100644
--- a/target/hexagon/reg_fields_def.h.inc
+++ b/target/hexagon/reg_fields_def.h.inc
@@ -136,6 +136,13 @@ DEF_REG_FIELD(CCR_VV1, 29, 1)
 DEF_REG_FIELD(CCR_VV2, 30, 1)
 DEF_REG_FIELD(CCR_VV3, 31, 1)
 
+/* GSR fields */
+DEF_REG_FIELD(GSR_CAUSE, 0, 16)
+DEF_REG_FIELD(GSR_CFI, 28, 1)
+DEF_REG_FIELD(GSR_SS, 29, 1)
+DEF_REG_FIELD(GSR_IE, 30, 1)
+DEF_REG_FIELD(GSR_UM, 31, 1)
+
 /* ISDB ST fields */
 DEF_REG_FIELD(ISDBST_WAITRUN, 24, 8)
 DEF_REG_FIELD(ISDBST_ONOFF, 16, 8)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 0a677840bcb..efaf569c6f8 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -475,6 +475,11 @@ static void hexagon_cpu_realize(DeviceState *dev, Error 
**errp)
         error_setg(errp, "hexagon cpu requires 'tlb' link property to be set");
         return;
     }
+    if (!HEXAGON_CPU(dev)->l2vic) {
+        error_setg(errp,
+                   "hexagon cpu requires 'l2vic' link property to be set");
+        return;
+    }
 #endif
 
     qemu_init_vcpu(cs);
diff --git a/target/hexagon/hex_interrupts.c b/target/hexagon/hex_interrupts.c
index 3534481da24..ea1ba0903dd 100644
--- a/target/hexagon/hex_interrupts.c
+++ b/target/hexagon/hex_interrupts.c
@@ -11,6 +11,7 @@
 #include "cpu_helper.h"
 #include "exec/cpu-interrupt.h"
 #include "hex_interrupts.h"
+#include "hw/intc/hex-l2vic.h"
 #include "macros.h"
 #include "sys_macros.h"
 #include "system/cpus.h"
@@ -215,19 +216,75 @@ static void restore_state(CPUHexagonState *env, bool 
int_accepted)
     }
 }
 
+static bool int_should_dtg(CPUHexagonState *env, int int_num)
+{
+    uint32_t ccr = env->t_sreg[HEX_SREG_CCR];
+
+    switch (int_num) {
+    case 3:
+        if (!GET_FIELD(CCR_VV1, ccr)) {
+            return false;
+        }
+        break;
+    case 4:
+        if (!GET_FIELD(CCR_VV2, ccr)) {
+            return false;
+        }
+        break;
+    case 5:
+        if (!GET_FIELD(CCR_VV3, ccr)) {
+            return false;
+        }
+        break;
+    default:
+        return false;
+    }
+
+    return GET_FIELD(CCR_GIE, ccr);
+}
+
+static void guest_interrupt_entry(CPUHexagonState *env, uint32_t cause,
+                                  uint32_t event_pc)
+{
+    uint32_t old_ssr = env->t_sreg[HEX_SREG_SSR];
+    uint32_t new_ssr = old_ssr;
+    uint32_t ccr = env->t_sreg[HEX_SREG_CCR];
+    uint32_t gsr = 0;
+
+    gsr = deposit32(gsr, reg_field_info[GSR_CAUSE].offset,
+                    reg_field_info[GSR_CAUSE].width, cause);
+    gsr = deposit32(gsr, reg_field_info[GSR_SS].offset,
+                    reg_field_info[GSR_SS].width,
+                    GET_SSR_FIELD(SSR_SS, old_ssr));
+    gsr = deposit32(gsr, reg_field_info[GSR_UM].offset,
+                    reg_field_info[GSR_UM].width,
+                    !GET_SSR_FIELD(SSR_GM, old_ssr));
+    gsr = deposit32(gsr, reg_field_info[GSR_IE].offset,
+                    reg_field_info[GSR_IE].width,
+                    GET_FIELD(CCR_GIE, ccr));
+    env->greg[HEX_GREG_GSR] = gsr;
+
+    fSET_FIELD(new_ssr, SSR_SS, 0);
+    fSET_FIELD(new_ssr, SSR_GM, 1);
+    env->t_sreg[HEX_SREG_SSR] = new_ssr;
+    hexagon_modify_ssr(env, new_ssr, old_ssr);
+
+    SET_SYSTEM_FIELD(env, HEX_SREG_CCR, CCR_GIE, 0);
+    env->greg[HEX_GREG_GELR] = event_pc;
+    env->gpr[HEX_REG_PC] = env->t_sreg[HEX_SREG_GEVB] |
+                           (HEX_EVENT_INT0 << 2);
+}
+
 static void hex_accept_int(CPUHexagonState *env, int int_num)
 {
     CPUState *cs = env_cpu(env);
     HexagonCPU *cpu = env_archcpu(env);
-    uint32_t evb =
-        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB,
-                               env->threadId);
     const int exe_mode = get_exe_mode(env);
     const bool in_wait_mode = exe_mode == HEX_EXE_MODE_WAIT;
+    uint32_t elr;
 
     set_ipend_bit(env, int_num, 0);
     set_iad_bit(env, int_num, 1);
-    set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num);
     cs->exception_index = HEX_EVENT_INT0 + int_num;
     env->cause_code = HEX_EVENT_INT0 + int_num;
     clear_pending_locks(env);
@@ -235,15 +292,31 @@ static void hex_accept_int(CPUHexagonState *env, int 
int_num)
         qemu_log_mask(CPU_LOG_INT,
             "%s: thread " TARGET_FMT_ld " resuming, exiting WAIT mode\n",
             __func__, env->threadId);
-        set_elr(env, env->wait_next_pc);
+        elr = env->wait_next_pc;
         clear_wait_mode(env);
         cs->halted = false;
     } else if (env->k0_lock_state == HEX_LOCK_WAITING) {
         g_assert_not_reached();
     } else {
-        set_elr(env, env->gpr[HEX_REG_PC]);
+        elr = env->gpr[HEX_REG_PC];
+    }
+
+    if (int_should_dtg(env, int_num)) {
+        int vic_group = int_num - 2;
+        uint32_t vid_packed = l2vic_read_vid(cpu->l2vic, vic_group / 2);
+        uint32_t vid = extract32(vid_packed,
+                                 (vic_group & 1) ? 16 : 0, 16);
+
+        guest_interrupt_entry(env, vid, elr);
+    } else {
+        uint32_t evb =
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB,
+                                   env->threadId);
+
+        set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num);
+        set_elr(env, elr);
+        env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2);
     }
-    env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2);
     if (get_ipend(env) == 0) {
         restore_state(env, true);
     }
-- 
2.34.1

Reply via email to