void cec_require_supervisor (SIM_CPU *cpu) { /* Do not call _cec_require_supervisor() to avoid CEC_STATE() as that macro requires OS operating mode. */ if (cec_is_user_mode (cpu)) cec_exception (cpu, VEC_ILL_RES); }
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; }
void cec_return (SIM_CPU *cpu, int ivg) { SIM_DESC sd = CPU_STATE (cpu); struct bfin_cec *cec; bool snen; int curr_ivg; bu32 oldpc, newpc; oldpc = PCREG; BFIN_CPU_STATE.did_jump = true; if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT) { SET_PCREG (cec_read_ret_reg (cpu, ivg)); TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC"); return; } cec = CEC_STATE (cpu); /* XXX: This isn't entirely correct ... */ cec->ipend &= ~IVG_EMU_B; curr_ivg = _cec_get_ivg (cec); if (curr_ivg == -1) curr_ivg = IVG_USER; if (ivg == -1) ivg = curr_ivg; TRACE_EVENTS (cpu, "returning from EVT%i (should be EVT%i)", curr_ivg, ivg); /* Not allowed to return from usermode. */ if (curr_ivg == IVG_USER) cec_exception (cpu, VEC_ILL_RES); if (ivg > IVG15 || ivg < 0) sim_io_error (sd, "%s: ivg %i out of range !", __func__, ivg); _cec_require_supervisor (cpu, cec); switch (ivg) { case IVG_EMU: /* RTE -- only valid in emulation mode. */ /* XXX: What does the hardware do ? */ if (curr_ivg != IVG_EMU) cec_exception (cpu, VEC_ILL_RES); break; case IVG_NMI: /* RTN -- only valid in NMI. */ /* XXX: What does the hardware do ? */ if (curr_ivg != IVG_NMI) cec_exception (cpu, VEC_ILL_RES); break; case IVG_EVX: /* RTX -- only valid in exception. */ /* XXX: What does the hardware do ? */ if (curr_ivg != IVG_EVX) cec_exception (cpu, VEC_ILL_RES); break; default: /* RTI -- not valid in emulation, nmi, exception, or user. */ /* XXX: What does the hardware do ? */ if (curr_ivg == IVG_EMU || curr_ivg == IVG_NMI || curr_ivg == IVG_EVX || curr_ivg == IVG_USER) cec_exception (cpu, VEC_ILL_RES); break; case IVG_IRPTEN: /* XXX: Is this even possible ? */ excp_to_sim_halt (sim_stopped, SIM_SIGABRT); break; } newpc = cec_read_ret_reg (cpu, ivg); /* XXX: Does this nested trick work on EMU/NMI/EVX ? */ snen = (newpc & 1); /* XXX: Delayed clear shows bad PCREG register trace above ? */ SET_PCREG (newpc & ~1); TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (from EVT%i)", ivg); /* Update ipend after the TRACE_BRANCH so dv-bfin_trace knows current CEC state wrt overflow. */ if (!snen) cec->ipend &= ~(1 << ivg); /* Disable global interrupt mask to let any interrupt take over, but only when we were already in a RTI level. Only way we could have raised at that point is if it was cleared in the first place. */ if (ivg >= IVG_IVHW || ivg == IVG_RST) cec_irpten_disable (cpu, cec); /* When going from super to user, we clear LSB in LB regs in case it was set on the transition up. Also need to load SP alias with USP. */ if (_cec_get_ivg (cec) == -1) { int i; for (i = 0; i < 2; ++i) if (LBREG (i) & 1) SET_LBREG (i, LBREG (i) & ~1); SET_KSPREG (SPREG); SET_SPREG (USPREG); } /* Check for pending interrupts before we return to usermode. */ _cec_check_pending (cpu, cec); }
static void _cec_require_supervisor (SIM_CPU *cpu, struct bfin_cec *cec) { if (_cec_is_user_mode (cec)) cec_exception (cpu, VEC_ILL_RES); }