Ejemplo n.º 1
0
int
cec_get_ivg (SIM_CPU *cpu)
{
  switch (STATE_ENVIRONMENT (CPU_STATE (cpu)))
    {
    case OPERATING_ENVIRONMENT:
      return _cec_get_ivg (CEC_STATE (cpu));
    default:
      return IVG_USER;
    }
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
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));
}