[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