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