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