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