Commit df51ba3811 for qemu.org
commit df51ba38113144da88ad91d53a3c9c1acfa0f4ed
Author: Brian Cain <brian.cain@oss.qualcomm.com>
Date: Tue Sep 1 10:33:51 2026 -0700
target/hexagon: implement direct-to-guest interrupt delivery
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.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index e4138df947..0530ec3ca9 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 3534481da2..7dde1294b2 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,79 @@ static void restore_state(CPUHexagonState *env, bool int_accepted)
}
}
+/*
+ * Direct-to-guest interrupts bypass the need for monitor-mode
+ * forwarding of interrupts into the guest OS.
+ */
+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 +296,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);
}
diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc
index d2c706d56b..29497fbcc4 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)