static sim_cia step_once (SIM_CPU *cpu) { SIM_DESC sd = CPU_STATE (cpu); bu32 insn_len, oldpc = PCREG; int i; bool ssstep; if (TRACE_ANY_P (cpu)) trace_prefix (sd, cpu, NULL_CIA, oldpc, TRACE_LINENUM_P (cpu), NULL, 0, " "); /* Use a space for gcc warnings. */ /* Handle hardware single stepping when lower than EVT3, and when SYSCFG has already had the SSSTEP bit enabled. */ ssstep = false; if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT && (SYSCFGREG & SYSCFG_SSSTEP)) { int ivg = cec_get_ivg (cpu); if (ivg == -1 || ivg > 3) ssstep = true; } #if 0 /* XXX: Is this what happens on the hardware ? */ if (cec_get_ivg (cpu) == EVT_EMU) cec_return (cpu, EVT_EMU); #endif BFIN_CPU_STATE.did_jump = false; insn_len = interp_insn_bfin (cpu, oldpc); /* If we executed this insn successfully, then we always decrement the loop counter. We don't want to update the PC though if the last insn happened to be a change in code flow (jump/etc...). */ if (!BFIN_CPU_STATE.did_jump) SET_PCREG (hwloop_get_next_pc (cpu, oldpc, insn_len)); for (i = 1; i >= 0; --i) if (LCREG (i) && oldpc == LBREG (i)) { SET_LCREG (i, LCREG (i) - 1); if (LCREG (i)) break; } ++ PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu)); /* Handle hardware single stepping only if we're still lower than EVT3. XXX: May not be entirely correct wrt EXCPT insns. */ if (ssstep) { int ivg = cec_get_ivg (cpu); if (ivg == -1 || ivg > 3) { INSN_LEN = 0; cec_exception (cpu, VEC_STEP); } } return oldpc; }
static void _cec_raise (SIM_CPU *cpu, struct bfin_cec *cec, int ivg) { SIM_DESC sd = CPU_STATE (cpu); int curr_ivg = _cec_get_ivg (cec); bool snen; bool irpten; TRACE_EVENTS (cpu, "processing request for EVT%i while at EVT%i", ivg, curr_ivg); irpten = (cec->ipend & IVG_IRPTEN_B); snen = (SYSCFGREG & SYSCFG_SNEN); if (curr_ivg == -1) curr_ivg = IVG_USER; /* Just check for higher latched interrupts. */ if (ivg == -1) { if (irpten) goto done; /* All interrupts are masked anyways. */ ivg = __cec_get_ivg (cec->ilat & cec->imask); if (ivg < 0) goto done; /* Nothing latched. */ if (ivg > curr_ivg) goto done; /* Nothing higher latched. */ if (!snen && ivg == curr_ivg) goto done; /* Self nesting disabled. */ /* Still here, so fall through to raise to higher pending. */ } cec->ilat |= (1 << ivg); if (ivg <= IVG_EVX) { /* These two are always processed. */ if (ivg == IVG_EMU || ivg == IVG_RST) goto process_int; /* Anything lower might trigger a double fault. */ if (curr_ivg <= ivg) { /* Double fault ! :( */ SET_EXCAUSE (VEC_UNCOV); /* XXX: SET_RETXREG (...); */ sim_io_error (sd, "%s: double fault at 0x%08x ! :(", __func__, PCREG); excp_to_sim_halt (sim_stopped, SIM_SIGABRT); } /* No double fault -> always process. */ goto process_int; } else if (irpten && curr_ivg != IVG_USER) { /* Interrupts are globally masked. */ } else if (!(cec->imask & (1 << ivg))) { /* This interrupt is masked. */ } else if (ivg < curr_ivg || (snen && ivg == curr_ivg)) { /* Do transition! */ bu32 oldpc; process_int: cec->ipend |= (1 << ivg); cec->ilat &= ~(1 << ivg); /* Interrupts are processed in between insns which means the return point is the insn-to-be-executed (which is the current PC). But exceptions are handled while executing an insn, so we may have to advance the PC ourselves when setting RETX. XXX: Advancing the PC should only be for "service" exceptions, and handling them after executing the insn should be OK, which means we might be able to use the event interface for it. */ oldpc = PCREG; switch (ivg) { case IVG_EMU: /* Signal the JTAG ICE. */ /* XXX: what happens with 'raise 0' ? */ SET_RETEREG (oldpc); excp_to_sim_halt (sim_stopped, SIM_SIGTRAP); /* XXX: Need an easy way for gdb to signal it isnt here. */ cec->ipend &= ~IVG_EMU_B; break; case IVG_RST: /* Have the core reset simply exit (i.e. "shutdown"). */ excp_to_sim_halt (sim_exited, 0); break; case IVG_NMI: /* XXX: Should check this. */ SET_RETNREG (oldpc); break; case IVG_EVX: /* Non-service exceptions point to the excepting instruction. */ if (EXCAUSE >= 0x20) SET_RETXREG (oldpc); else { bu32 nextpc = hwloop_get_next_pc (cpu, oldpc, INSN_LEN); SET_RETXREG (nextpc); } break; case IVG_IRPTEN: /* XXX: what happens with 'raise 4' ? */ sim_io_error (sd, "%s: what to do with 'raise 4' ?", __func__); break; default: SET_RETIREG (oldpc | (ivg == curr_ivg ? 1 : 0)); break; } /* If EVT_OVERRIDE is in effect (IVG7+), use the reset address. */ if ((cec->evt_override & 0xff80) & (1 << ivg)) SET_PCREG (cec_get_reset_evt (cpu)); else SET_PCREG (cec_get_evt (cpu, ivg)); TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (to EVT%i):", ivg); BFIN_CPU_STATE.did_jump = true; /* Enable the global interrupt mask upon interrupt entry. */ if (ivg >= IVG_IVHW) cec_irpten_enable (cpu, cec); } /* When moving between states, don't let internal states bleed through. */ DIS_ALGN_EXPT &= ~1; /* When going from user to super, we set LSB in LB regs to avoid misbehavior and/or malicious code. Also need to load SP alias with KSP. */ if (curr_ivg == IVG_USER) { int i; for (i = 0; i < 2; ++i) if (!(LBREG (i) & 1)) SET_LBREG (i, LBREG (i) | 1); SET_USPREG (SPREG); SET_SPREG (KSPREG); } done: TRACE_EVENTS (cpu, "now at EVT%i", _cec_get_ivg (cec)); }