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, ®)) 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 ®.address, 187 1.1 christos &attach_space, &attach_address, me); 188 1.1 christos hw_unit_size_to_attach_size (hw_parent (me), ®.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