xref: /netbsd-src/external/gpl3/gdb/dist/sim/bfin/dv-bfin_cec.c (revision 8450a7c42673d65e3b1f6560d3b6ecd317a6cbe8)
1 /* Blackfin Core Event Controller (CEC) model.
2 
3    Copyright (C) 2010-2016 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