Home | History | Annotate | Line # | Download | only in bfin
dv-bfin_cec.c revision 1.7
      1 /* Blackfin Core Event Controller (CEC) model.
      2 
      3    Copyright (C) 2010-2017 Free Software Foundation, Inc.
      4    Contributed by Analog Devices, Inc.
      5 
      6    This file is part of simulators.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 3 of the License, or
     11    (at your option) any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     20 
     21 #include "config.h"
     22 
     23 #include "sim-main.h"
     24 #include "devices.h"
     25 #include "dv-bfin_cec.h"
     26 #include "dv-bfin_evt.h"
     27 #include "dv-bfin_mmu.h"
     28 
     29 struct bfin_cec
     30 {
     31   bu32 base;
     32   SIM_CPU *cpu;
     33   struct hw *me;
     34   struct hw_event *pending;
     35 
     36   /* Order after here is important -- matches hardware MMR layout.  */
     37   bu32 evt_override, imask, ipend, ilat, iprio;
     38 };
     39 #define mmr_base()      offsetof(struct bfin_cec, evt_override)
     40 #define mmr_offset(mmr) (offsetof(struct bfin_cec, mmr) - mmr_base())
     41 
     42 static const char * const mmr_names[] =
     43 {
     44   "EVT_OVERRIDE", "IMASK", "IPEND", "ILAT", "IPRIO",
     45 };
     46 #define mmr_name(off) mmr_names[(off) / 4]
     47 
     48 static void _cec_raise (SIM_CPU *, struct bfin_cec *, int);
     49 
     50 static void
     51 bfin_cec_hw_event_callback (struct hw *me, void *data)
     52 {
     53   struct bfin_cec *cec = data;
     54   hw_event_queue_deschedule (me, cec->pending);
     55   _cec_raise (cec->cpu, cec, -1);
     56   cec->pending = NULL;
     57 }
     58 static void
     59 bfin_cec_check_pending (struct hw *me, struct bfin_cec *cec)
     60 {
     61   if (cec->pending)
     62     return;
     63   cec->pending = hw_event_queue_schedule (me, 0, bfin_cec_hw_event_callback, cec);
     64 }
     65 static void
     66 _cec_check_pending (SIM_CPU *cpu, struct bfin_cec *cec)
     67 {
     68   bfin_cec_check_pending (cec->me, cec);
     69 }
     70 
     71 static void
     72 _cec_imask_write (struct bfin_cec *cec, bu32 value)
     73 {
     74   cec->imask = (value & IVG_MASKABLE_B) | (cec->imask & IVG_UNMASKABLE_B);
     75 }
     76 
     77 static unsigned
     78 bfin_cec_io_write_buffer (struct hw *me, const void *source,
     79 			  int space, address_word addr, unsigned nr_bytes)
     80 {
     81   struct bfin_cec *cec = hw_data (me);
     82   bu32 mmr_off;
     83   bu32 value;
     84 
     85   /* Invalid access mode is higher priority than missing register.  */
     86   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true))
     87     return 0;
     88 
     89   value = dv_load_4 (source);
     90   mmr_off = addr - cec->base;
     91 
     92   HW_TRACE_WRITE ();
     93 
     94   switch (mmr_off)
     95     {
     96     case mmr_offset(evt_override):
     97       cec->evt_override = value;
     98       break;
     99     case mmr_offset(imask):
    100       _cec_imask_write (cec, value);
    101       bfin_cec_check_pending (me, cec);
    102       break;
    103     case mmr_offset(ipend):
    104       /* Read-only register.  */
    105       break;
    106     case mmr_offset(ilat):
    107       dv_w1c_4 (&cec->ilat, value, 0xffee);
    108       break;
    109     case mmr_offset(iprio):
    110       cec->iprio = (value & IVG_UNMASKABLE_B);
    111       break;
    112     }
    113 
    114   return nr_bytes;
    115 }
    116 
    117 static unsigned
    118 bfin_cec_io_read_buffer (struct hw *me, void *dest,
    119 			 int space, address_word addr, unsigned nr_bytes)
    120 {
    121   struct bfin_cec *cec = hw_data (me);
    122   bu32 mmr_off;
    123   bu32 *valuep;
    124 
    125   /* Invalid access mode is higher priority than missing register.  */
    126   if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false))
    127     return 0;
    128 
    129   mmr_off = addr - cec->base;
    130   valuep = (void *)((unsigned long)cec + mmr_base() + mmr_off);
    131 
    132   HW_TRACE_READ ();
    133 
    134   dv_store_4 (dest, *valuep);
    135 
    136   return nr_bytes;
    137 }
    138 
    139 static const struct hw_port_descriptor bfin_cec_ports[] =
    140 {
    141   { "emu",   IVG_EMU,   0, input_port, },
    142   { "rst",   IVG_RST,   0, input_port, },
    143   { "nmi",   IVG_NMI,   0, input_port, },
    144   { "evx",   IVG_EVX,   0, input_port, },
    145   { "ivhw",  IVG_IVHW,  0, input_port, },
    146   { "ivtmr", IVG_IVTMR, 0, input_port, },
    147   { "ivg7",  IVG7,      0, input_port, },
    148   { "ivg8",  IVG8,      0, input_port, },
    149   { "ivg9",  IVG9,      0, input_port, },
    150   { "ivg10", IVG10,     0, input_port, },
    151   { "ivg11", IVG11,     0, input_port, },
    152   { "ivg12", IVG12,     0, input_port, },
    153   { "ivg13", IVG13,     0, input_port, },
    154   { "ivg14", IVG14,     0, input_port, },
    155   { "ivg15", IVG15,     0, input_port, },
    156   { NULL, 0, 0, 0, },
    157 };
    158 
    159 static void
    160 bfin_cec_port_event (struct hw *me, int my_port, struct hw *source,
    161 		     int source_port, int level)
    162 {
    163   struct bfin_cec *cec = hw_data (me);
    164   _cec_raise (cec->cpu, cec, my_port);
    165 }
    166 
    167 static void
    168 attach_bfin_cec_regs (struct hw *me, struct bfin_cec *cec)
    169 {
    170   address_word attach_address;
    171   int attach_space;
    172   unsigned attach_size;
    173   reg_property_spec reg;
    174 
    175   if (hw_find_property (me, "reg") == NULL)
    176     hw_abort (me, "Missing \"reg\" property");
    177 
    178   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
    179     hw_abort (me, "\"reg\" property must contain three addr/size entries");
    180 
    181   hw_unit_address_to_attach_address (hw_parent (me),
    182 				     &reg.address,
    183 				     &attach_space, &attach_address, me);
    184   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
    185 
    186   if (attach_size != BFIN_COREMMR_CEC_SIZE)
    187     hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_CEC_SIZE);
    188 
    189   hw_attach_address (hw_parent (me),
    190 		     0, attach_space, attach_address, attach_size, me);
    191 
    192   cec->base = attach_address;
    193   /* XXX: should take from the device tree.  */
    194   cec->cpu = STATE_CPU (hw_system (me), 0);
    195   cec->me = me;
    196 }
    197 
    198 static void
    199 bfin_cec_finish (struct hw *me)
    200 {
    201   struct bfin_cec *cec;
    202 
    203   cec = HW_ZALLOC (me, struct bfin_cec);
    204 
    205   set_hw_data (me, cec);
    206   set_hw_io_read_buffer (me, bfin_cec_io_read_buffer);
    207   set_hw_io_write_buffer (me, bfin_cec_io_write_buffer);
    208   set_hw_ports (me, bfin_cec_ports);
    209   set_hw_port_event (me, bfin_cec_port_event);
    210 
    211   attach_bfin_cec_regs (me, cec);
    212 
    213   /* Initialize the CEC.  */
    214   cec->imask = IVG_UNMASKABLE_B;
    215   cec->ipend = IVG_RST_B | IVG_IRPTEN_B;
    216 }
    217 
    218 const struct hw_descriptor dv_bfin_cec_descriptor[] =
    219 {
    220   {"bfin_cec", bfin_cec_finish,},
    221   {NULL, NULL},
    222 };
    223 
    224 static const char * const excp_decoded[] =
    225 {
    226   [VEC_SYS        ] = "Custom exception 0 (system call)",
    227   [VEC_EXCPT01    ] = "Custom exception 1 (software breakpoint)",
    228   [VEC_EXCPT02    ] = "Custom exception 2 (KGDB hook)",
    229   [VEC_EXCPT03    ] = "Custom exception 3 (userspace stack overflow)",
    230   [VEC_EXCPT04    ] = "Custom exception 4 (dump trace buffer)",
    231   [VEC_EXCPT05    ] = "Custom exception 5",
    232   [VEC_EXCPT06    ] = "Custom exception 6",
    233   [VEC_EXCPT07    ] = "Custom exception 7",
    234   [VEC_EXCPT08    ] = "Custom exception 8",
    235   [VEC_EXCPT09    ] = "Custom exception 9",
    236   [VEC_EXCPT10    ] = "Custom exception 10",
    237   [VEC_EXCPT11    ] = "Custom exception 11",
    238   [VEC_EXCPT12    ] = "Custom exception 12",
    239   [VEC_EXCPT13    ] = "Custom exception 13",
    240   [VEC_EXCPT14    ] = "Custom exception 14",
    241   [VEC_EXCPT15    ] = "Custom exception 15",
    242   [VEC_STEP       ] = "Hardware single step",
    243   [VEC_OVFLOW     ] = "Trace buffer overflow",
    244   [VEC_UNDEF_I    ] = "Undefined instruction",
    245   [VEC_ILGAL_I    ] = "Illegal instruction combo (multi-issue)",
    246   [VEC_CPLB_VL    ] = "DCPLB protection violation",
    247   [VEC_MISALI_D   ] = "Unaligned data access",
    248   [VEC_UNCOV      ] = "Unrecoverable event (double fault)",
    249   [VEC_CPLB_M     ] = "DCPLB miss",
    250   [VEC_CPLB_MHIT  ] = "Multiple DCPLB hit",
    251   [VEC_WATCH      ] = "Watchpoint match",
    252   [VEC_ISTRU_VL   ] = "ADSP-BF535 only",
    253   [VEC_MISALI_I   ] = "Unaligned instruction access",
    254   [VEC_CPLB_I_VL  ] = "ICPLB protection violation",
    255   [VEC_CPLB_I_M   ] = "ICPLB miss",
    256   [VEC_CPLB_I_MHIT] = "Multiple ICPLB hit",
    257   [VEC_ILL_RES    ] = "Illegal supervisor resource",
    258 };
    259 
    260 #define CEC_STATE(cpu) DV_STATE_CACHED (cpu, cec)
    261 
    262 #define __cec_get_ivg(val) (ffs ((val) & ~IVG_IRPTEN_B) - 1)
    263 #define _cec_get_ivg(cec) __cec_get_ivg ((cec)->ipend & ~IVG_EMU_B)
    264 
    265 int
    266 cec_get_ivg (SIM_CPU *cpu)
    267 {
    268   switch (STATE_ENVIRONMENT (CPU_STATE (cpu)))
    269     {
    270     case OPERATING_ENVIRONMENT:
    271       return _cec_get_ivg (CEC_STATE (cpu));
    272     default:
    273       return IVG_USER;
    274     }
    275 }
    276 
    277 static bool
    278 _cec_is_supervisor_mode (struct bfin_cec *cec)
    279 {
    280   return (cec->ipend & ~(IVG_EMU_B | IVG_IRPTEN_B));
    281 }
    282 bool
    283 cec_is_supervisor_mode (SIM_CPU *cpu)
    284 {
    285   switch (STATE_ENVIRONMENT (CPU_STATE (cpu)))
    286     {
    287     case OPERATING_ENVIRONMENT:
    288       return _cec_is_supervisor_mode (CEC_STATE (cpu));
    289     case USER_ENVIRONMENT:
    290       return false;
    291     default:
    292       return true;
    293     }
    294 }
    295 static bool
    296 _cec_is_user_mode (struct bfin_cec *cec)
    297 {
    298   return !_cec_is_supervisor_mode (cec);
    299 }
    300 bool
    301 cec_is_user_mode (SIM_CPU *cpu)
    302 {
    303   return !cec_is_supervisor_mode (cpu);
    304 }
    305 static void
    306 _cec_require_supervisor (SIM_CPU *cpu, struct bfin_cec *cec)
    307 {
    308   if (_cec_is_user_mode (cec))
    309     cec_exception (cpu, VEC_ILL_RES);
    310 }
    311 void
    312 cec_require_supervisor (SIM_CPU *cpu)
    313 {
    314   /* Do not call _cec_require_supervisor() to avoid CEC_STATE()
    315      as that macro requires OS operating mode.  */
    316   if (cec_is_user_mode (cpu))
    317     cec_exception (cpu, VEC_ILL_RES);
    318 }
    319 
    320 #define excp_to_sim_halt(reason, sigrc) \
    321   sim_engine_halt (CPU_STATE (cpu), cpu, NULL, PCREG, reason, sigrc)
    322 void
    323 cec_exception (SIM_CPU *cpu, int excp)
    324 {
    325   SIM_DESC sd = CPU_STATE (cpu);
    326   int sigrc = -1;
    327 
    328   TRACE_EVENTS (cpu, "processing exception %#x in EVT%i", excp,
    329 		cec_get_ivg (cpu));
    330 
    331   /* Ideally what would happen here for real hardware exceptions (not
    332      fake sim ones) is that:
    333       - For service exceptions (excp <= 0x11):
    334          RETX is the _next_ PC which can be tricky with jumps/hardware loops/...
    335       - For error exceptions (excp > 0x11):
    336          RETX is the _current_ PC (i.e. the one causing the exception)
    337       - PC is loaded with EVT3 MMR
    338       - ILAT/IPEND in CEC is updated depending on current IVG level
    339       - the fault address MMRs get updated with data/instruction info
    340       - Execution continues on in the EVT3 handler  */
    341 
    342   /* Handle simulator exceptions first.  */
    343   switch (excp)
    344     {
    345     case VEC_SIM_HLT:
    346       excp_to_sim_halt (sim_exited, 0);
    347       return;
    348     case VEC_SIM_ABORT:
    349       excp_to_sim_halt (sim_exited, 1);
    350       return;
    351     case VEC_SIM_TRAP:
    352       /* GDB expects us to step over EMUEXCPT.  */
    353       /* XXX: What about hwloops and EMUEXCPT at the end?
    354               Pretty sure gdb doesn't handle this already...  */
    355       SET_PCREG (PCREG + 2);
    356       /* Only trap when we are running in gdb.  */
    357       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
    358 	excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    359       return;
    360     case VEC_SIM_DBGA:
    361       /* If running in gdb, simply trap.  */
    362       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
    363 	excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    364       else
    365 	excp_to_sim_halt (sim_exited, 2);
    366     }
    367 
    368   if (excp <= 0x3f)
    369     {
    370       SET_EXCAUSE (excp);
    371       if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
    372 	{
    373 	  /* ICPLB regs always get updated.  */
    374 	  /* XXX: Should optimize this call path ...  */
    375 	  if (excp != VEC_MISALI_I && excp != VEC_MISALI_D
    376 	      && excp != VEC_CPLB_I_M && excp != VEC_CPLB_M
    377 	      && excp != VEC_CPLB_I_VL && excp != VEC_CPLB_VL
    378 	      && excp != VEC_CPLB_I_MHIT && excp != VEC_CPLB_MHIT)
    379 	    mmu_log_ifault (cpu);
    380 	  _cec_raise (cpu, CEC_STATE (cpu), IVG_EVX);
    381 	  /* We need to restart the engine so that we don't return
    382 	     and continue processing this bad insn.  */
    383 	  if (EXCAUSE >= 0x20)
    384 	    sim_engine_restart (sd, cpu, NULL, PCREG);
    385 	  return;
    386 	}
    387     }
    388 
    389   TRACE_EVENTS (cpu, "running virtual exception handler");
    390 
    391   switch (excp)
    392     {
    393     case VEC_SYS:
    394       bfin_syscall (cpu);
    395       break;
    396 
    397     case VEC_EXCPT01:	/* Userspace gdb breakpoint.  */
    398       sigrc = SIM_SIGTRAP;
    399       break;
    400 
    401     case VEC_UNDEF_I:	/* Undefined instruction.  */
    402       sigrc = SIM_SIGILL;
    403       break;
    404 
    405     case VEC_ILL_RES:	/* Illegal supervisor resource.  */
    406     case VEC_MISALI_I:	/* Misaligned instruction.  */
    407       sigrc = SIM_SIGBUS;
    408       break;
    409 
    410     case VEC_CPLB_M:
    411     case VEC_CPLB_I_M:
    412       sigrc = SIM_SIGSEGV;
    413       break;
    414 
    415     default:
    416       sim_io_eprintf (sd, "Unhandled exception %#x at 0x%08x (%s)\n",
    417 		      excp, PCREG, excp_decoded[excp]);
    418       sigrc = SIM_SIGILL;
    419       break;
    420     }
    421 
    422   if (sigrc != -1)
    423     excp_to_sim_halt (sim_stopped, sigrc);
    424 }
    425 
    426 bu32 cec_cli (SIM_CPU *cpu)
    427 {
    428   struct bfin_cec *cec;
    429   bu32 old_mask;
    430 
    431   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    432     return 0;
    433 
    434   cec = CEC_STATE (cpu);
    435   _cec_require_supervisor (cpu, cec);
    436 
    437   /* XXX: what about IPEND[4] ?  */
    438   old_mask = cec->imask;
    439   _cec_imask_write (cec, 0);
    440 
    441   TRACE_EVENTS (cpu, "CLI changed IMASK from %#x to %#x", old_mask, cec->imask);
    442 
    443   return old_mask;
    444 }
    445 
    446 void cec_sti (SIM_CPU *cpu, bu32 ints)
    447 {
    448   struct bfin_cec *cec;
    449   bu32 old_mask;
    450 
    451   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    452     return;
    453 
    454   cec = CEC_STATE (cpu);
    455   _cec_require_supervisor (cpu, cec);
    456 
    457   /* XXX: what about IPEND[4] ?  */
    458   old_mask = cec->imask;
    459   _cec_imask_write (cec, ints);
    460 
    461   TRACE_EVENTS (cpu, "STI changed IMASK from %#x to %#x", old_mask, cec->imask);
    462 
    463   /* Check for pending interrupts that are now enabled.  */
    464   _cec_check_pending (cpu, cec);
    465 }
    466 
    467 static void
    468 cec_irpten_enable (SIM_CPU *cpu, struct bfin_cec *cec)
    469 {
    470   /* Globally mask interrupts.  */
    471   TRACE_EVENTS (cpu, "setting IPEND[4] to globally mask interrupts");
    472   cec->ipend |= IVG_IRPTEN_B;
    473 }
    474 
    475 static void
    476 cec_irpten_disable (SIM_CPU *cpu, struct bfin_cec *cec)
    477 {
    478   /* Clear global interrupt mask.  */
    479   TRACE_EVENTS (cpu, "clearing IPEND[4] to not globally mask interrupts");
    480   cec->ipend &= ~IVG_IRPTEN_B;
    481 }
    482 
    483 static void
    484 _cec_raise (SIM_CPU *cpu, struct bfin_cec *cec, int ivg)
    485 {
    486   SIM_DESC sd = CPU_STATE (cpu);
    487   int curr_ivg = _cec_get_ivg (cec);
    488   bool snen;
    489   bool irpten;
    490 
    491   TRACE_EVENTS (cpu, "processing request for EVT%i while at EVT%i",
    492 		ivg, curr_ivg);
    493 
    494   irpten = (cec->ipend & IVG_IRPTEN_B);
    495   snen = (SYSCFGREG & SYSCFG_SNEN);
    496 
    497   if (curr_ivg == -1)
    498     curr_ivg = IVG_USER;
    499 
    500   /* Just check for higher latched interrupts.  */
    501   if (ivg == -1)
    502     {
    503       if (irpten)
    504 	goto done; /* All interrupts are masked anyways.  */
    505 
    506       ivg = __cec_get_ivg (cec->ilat & cec->imask);
    507       if (ivg < 0)
    508 	goto done; /* Nothing latched.  */
    509 
    510       if (ivg > curr_ivg)
    511 	goto done; /* Nothing higher latched.  */
    512 
    513       if (!snen && ivg == curr_ivg)
    514 	goto done; /* Self nesting disabled.  */
    515 
    516       /* Still here, so fall through to raise to higher pending.  */
    517     }
    518 
    519   cec->ilat |= (1 << ivg);
    520 
    521   if (ivg <= IVG_EVX)
    522     {
    523       /* These two are always processed.  */
    524       if (ivg == IVG_EMU || ivg == IVG_RST)
    525 	goto process_int;
    526 
    527       /* Anything lower might trigger a double fault.  */
    528       if (curr_ivg <= ivg)
    529 	{
    530 	  /* Double fault ! :(  */
    531 	  SET_EXCAUSE (VEC_UNCOV);
    532 	  /* XXX: SET_RETXREG (...);  */
    533 	  sim_io_error (sd, "%s: double fault at 0x%08x ! :(", __func__, PCREG);
    534 	  excp_to_sim_halt (sim_stopped, SIM_SIGABRT);
    535 	}
    536 
    537       /* No double fault -> always process.  */
    538       goto process_int;
    539     }
    540   else if (irpten && curr_ivg != IVG_USER)
    541     {
    542       /* Interrupts are globally masked.  */
    543     }
    544   else if (!(cec->imask & (1 << ivg)))
    545     {
    546       /* This interrupt is masked.  */
    547     }
    548   else if (ivg < curr_ivg || (snen && ivg == curr_ivg))
    549     {
    550       /* Do transition!  */
    551       bu32 oldpc;
    552 
    553  process_int:
    554       cec->ipend |= (1 << ivg);
    555       cec->ilat &= ~(1 << ivg);
    556 
    557       /* Interrupts are processed in between insns which means the return
    558          point is the insn-to-be-executed (which is the current PC).  But
    559          exceptions are handled while executing an insn, so we may have to
    560          advance the PC ourselves when setting RETX.
    561          XXX: Advancing the PC should only be for "service" exceptions, and
    562               handling them after executing the insn should be OK, which
    563               means we might be able to use the event interface for it.  */
    564 
    565       oldpc = PCREG;
    566       switch (ivg)
    567 	{
    568 	case IVG_EMU:
    569 	  /* Signal the JTAG ICE.  */
    570 	  /* XXX: what happens with 'raise 0' ?  */
    571 	  SET_RETEREG (oldpc);
    572 	  excp_to_sim_halt (sim_stopped, SIM_SIGTRAP);
    573 	  /* XXX: Need an easy way for gdb to signal it isnt here.  */
    574 	  cec->ipend &= ~IVG_EMU_B;
    575 	  break;
    576 	case IVG_RST:
    577 	  /* Have the core reset simply exit (i.e. "shutdown").  */
    578 	  excp_to_sim_halt (sim_exited, 0);
    579 	  break;
    580 	case IVG_NMI:
    581 	  /* XXX: Should check this.  */
    582 	  SET_RETNREG (oldpc);
    583 	  break;
    584 	case IVG_EVX:
    585 	  /* Non-service exceptions point to the excepting instruction.  */
    586 	  if (EXCAUSE >= 0x20)
    587 	    SET_RETXREG (oldpc);
    588 	  else
    589 	    {
    590 	      bu32 nextpc = hwloop_get_next_pc (cpu, oldpc, INSN_LEN);
    591 	      SET_RETXREG (nextpc);
    592 	    }
    593 
    594 	  break;
    595 	case IVG_IRPTEN:
    596 	  /* XXX: what happens with 'raise 4' ?  */
    597 	  sim_io_error (sd, "%s: what to do with 'raise 4' ?", __func__);
    598 	  break;
    599 	default:
    600 	  SET_RETIREG (oldpc | (ivg == curr_ivg ? 1 : 0));
    601 	  break;
    602 	}
    603 
    604       /* If EVT_OVERRIDE is in effect (IVG7+), use the reset address.  */
    605       if ((cec->evt_override & 0xff80) & (1 << ivg))
    606 	SET_PCREG (cec_get_reset_evt (cpu));
    607       else
    608 	SET_PCREG (cec_get_evt (cpu, ivg));
    609 
    610       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (to EVT%i):", ivg);
    611       BFIN_CPU_STATE.did_jump = true;
    612 
    613       /* Enable the global interrupt mask upon interrupt entry.  */
    614       if (ivg >= IVG_IVHW)
    615 	cec_irpten_enable (cpu, cec);
    616     }
    617 
    618   /* When moving between states, don't let internal states bleed through.  */
    619   DIS_ALGN_EXPT &= ~1;
    620 
    621   /* When going from user to super, we set LSB in LB regs to avoid
    622      misbehavior and/or malicious code.
    623      Also need to load SP alias with KSP.  */
    624   if (curr_ivg == IVG_USER)
    625     {
    626       int i;
    627       for (i = 0; i < 2; ++i)
    628 	if (!(LBREG (i) & 1))
    629 	  SET_LBREG (i, LBREG (i) | 1);
    630       SET_USPREG (SPREG);
    631       SET_SPREG (KSPREG);
    632     }
    633 
    634  done:
    635   TRACE_EVENTS (cpu, "now at EVT%i", _cec_get_ivg (cec));
    636 }
    637 
    638 static bu32
    639 cec_read_ret_reg (SIM_CPU *cpu, int ivg)
    640 {
    641   switch (ivg)
    642     {
    643     case IVG_EMU: return RETEREG;
    644     case IVG_NMI: return RETNREG;
    645     case IVG_EVX: return RETXREG;
    646     default:      return RETIREG;
    647     }
    648 }
    649 
    650 void
    651 cec_latch (SIM_CPU *cpu, int ivg)
    652 {
    653   struct bfin_cec *cec;
    654 
    655   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    656     {
    657       bu32 oldpc = PCREG;
    658       SET_PCREG (cec_read_ret_reg (cpu, ivg));
    659       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC");
    660       return;
    661     }
    662 
    663   cec = CEC_STATE (cpu);
    664   cec->ilat |= (1 << ivg);
    665   _cec_check_pending (cpu, cec);
    666 }
    667 
    668 void
    669 cec_hwerr (SIM_CPU *cpu, int hwerr)
    670 {
    671   SET_HWERRCAUSE (hwerr);
    672   cec_latch (cpu, IVG_IVHW);
    673 }
    674 
    675 void
    676 cec_return (SIM_CPU *cpu, int ivg)
    677 {
    678   SIM_DESC sd = CPU_STATE (cpu);
    679   struct bfin_cec *cec;
    680   bool snen;
    681   int curr_ivg;
    682   bu32 oldpc, newpc;
    683 
    684   oldpc = PCREG;
    685 
    686   BFIN_CPU_STATE.did_jump = true;
    687   if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
    688     {
    689       SET_PCREG (cec_read_ret_reg (cpu, ivg));
    690       BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC");
    691       return;
    692     }
    693 
    694   cec = CEC_STATE (cpu);
    695 
    696   /* XXX: This isn't entirely correct ...  */
    697   cec->ipend &= ~IVG_EMU_B;
    698 
    699   curr_ivg = _cec_get_ivg (cec);
    700   if (curr_ivg == -1)
    701     curr_ivg = IVG_USER;
    702   if (ivg == -1)
    703     ivg = curr_ivg;
    704 
    705   TRACE_EVENTS (cpu, "returning from EVT%i (should be EVT%i)", curr_ivg, ivg);
    706 
    707   /* Not allowed to return from usermode.  */
    708   if (curr_ivg == IVG_USER)
    709     cec_exception (cpu, VEC_ILL_RES);
    710 
    711   if (ivg > IVG15 || ivg < 0)
    712     sim_io_error (sd, "%s: ivg %i out of range !", __func__, ivg);
    713 
    714   _cec_require_supervisor (cpu, cec);
    715 
    716   switch (ivg)
    717     {
    718     case IVG_EMU:
    719       /* RTE -- only valid in emulation mode.  */
    720       /* XXX: What does the hardware do ?  */
    721       if (curr_ivg != IVG_EMU)
    722 	cec_exception (cpu, VEC_ILL_RES);
    723       break;
    724     case IVG_NMI:
    725       /* RTN -- only valid in NMI.  */
    726       /* XXX: What does the hardware do ?  */
    727       if (curr_ivg != IVG_NMI)
    728 	cec_exception (cpu, VEC_ILL_RES);
    729       break;
    730     case IVG_EVX:
    731       /* RTX -- only valid in exception.  */
    732       /* XXX: What does the hardware do ?  */
    733       if (curr_ivg != IVG_EVX)
    734 	cec_exception (cpu, VEC_ILL_RES);
    735       break;
    736     default:
    737       /* RTI -- not valid in emulation, nmi, exception, or user.  */
    738       /* XXX: What does the hardware do ?  */
    739       if (curr_ivg == IVG_EMU || curr_ivg == IVG_NMI
    740 	  || curr_ivg == IVG_EVX || curr_ivg == IVG_USER)
    741 	cec_exception (cpu, VEC_ILL_RES);
    742       break;
    743     case IVG_IRPTEN:
    744       /* XXX: Is this even possible ?  */
    745       excp_to_sim_halt (sim_stopped, SIM_SIGABRT);
    746       break;
    747     }
    748   newpc = cec_read_ret_reg (cpu, ivg);
    749 
    750   /* XXX: Does this nested trick work on EMU/NMI/EVX ?  */
    751   snen = (newpc & 1);
    752   /* XXX: Delayed clear shows bad PCREG register trace above ?  */
    753   SET_PCREG (newpc & ~1);
    754 
    755   BFIN_TRACE_BRANCH (cpu, oldpc, PCREG, -1, "CEC changed PC (from EVT%i)", ivg);
    756 
    757   /* Update ipend after the BFIN_TRACE_BRANCH so dv-bfin_trace
    758      knows current CEC state wrt overflow.  */
    759   if (!snen)
    760     cec->ipend &= ~(1 << ivg);
    761 
    762   /* Disable global interrupt mask to let any interrupt take over, but
    763      only when we were already in a RTI level.  Only way we could have
    764      raised at that point is if it was cleared in the first place.  */
    765   if (ivg >= IVG_IVHW || ivg == IVG_RST)
    766     cec_irpten_disable (cpu, cec);
    767 
    768   /* When going from super to user, we clear LSB in LB regs in case
    769      it was set on the transition up.
    770      Also need to load SP alias with USP.  */
    771   if (_cec_get_ivg (cec) == -1)
    772     {
    773       int i;
    774       for (i = 0; i < 2; ++i)
    775 	if (LBREG (i) & 1)
    776 	  SET_LBREG (i, LBREG (i) & ~1);
    777       SET_KSPREG (SPREG);
    778       SET_SPREG (USPREG);
    779     }
    780 
    781   /* Check for pending interrupts before we return to usermode.  */
    782   _cec_check_pending (cpu, cec);
    783 }
    784 
    785 void
    786 cec_push_reti (SIM_CPU *cpu)
    787 {
    788   /* XXX: Need to check hardware with popped RETI value
    789      and bit 1 is set (when handling nested interrupts).
    790      Also need to check behavior wrt SNEN in SYSCFG.  */
    791   struct bfin_cec *cec;
    792 
    793   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    794     return;
    795 
    796   TRACE_EVENTS (cpu, "pushing RETI");
    797 
    798   cec = CEC_STATE (cpu);
    799   cec_irpten_disable (cpu, cec);
    800   /* Check for pending interrupts.  */
    801   _cec_check_pending (cpu, cec);
    802 }
    803 
    804 void
    805 cec_pop_reti (SIM_CPU *cpu)
    806 {
    807   /* XXX: Need to check hardware with popped RETI value
    808      and bit 1 is set (when handling nested interrupts).
    809      Also need to check behavior wrt SNEN in SYSCFG.  */
    810   struct bfin_cec *cec;
    811 
    812   if (STATE_ENVIRONMENT (CPU_STATE (cpu)) != OPERATING_ENVIRONMENT)
    813     return;
    814 
    815   TRACE_EVENTS (cpu, "popping RETI");
    816 
    817   cec = CEC_STATE (cpu);
    818   cec_irpten_enable (cpu, cec);
    819 }
    820