On 8/20/2026 2:59 PM, Brian Cain wrote:
>
> On 8/20/2026 1:57 PM, Pierrick Bouvier wrote:
>> On 8/18/2026 6:31 PM, Brian Cain wrote:
>>> 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)
>>> +{
>> What does dtg means?
>
> "dtg" is "direct-to-guest" interrupts. This is an architectural feature
> to raise interrupts directly in the guest instead of the monitor/VMM,
> saving the latency of having to manually propagate interrupts to the guest.
>
> Maybe this is a good case for a clarifying comment on `int_should_dtg()`?
>
Yes it would help to add the paragraph above to describe what it is.
>>
>>> + 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);
>>> }