[PATCH 09/11] target/hexagon: implement direct-to-guest interrupt delivery

Brian Cain <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.