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