1 1.1 christos /* OpenRISC exception, interrupts, syscall and trap support 2 1.1.1.4 christos Copyright (C) 2017-2024 Free Software Foundation, Inc. 3 1.1 christos 4 1.1 christos This file is part of GDB, the GNU debugger. 5 1.1 christos 6 1.1 christos This program is free software; you can redistribute it and/or modify 7 1.1 christos it under the terms of the GNU General Public License as published by 8 1.1 christos the Free Software Foundation; either version 3 of the License, or 9 1.1 christos (at your option) any later version. 10 1.1 christos 11 1.1 christos This program is distributed in the hope that it will be useful, 12 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 13 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 1.1 christos GNU General Public License for more details. 15 1.1 christos 16 1.1 christos You should have received a copy of the GNU General Public License 17 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 18 1.1 christos 19 1.1.1.3 christos /* This must come before any other includes. */ 20 1.1.1.3 christos #include "defs.h" 21 1.1.1.3 christos 22 1.1 christos #define WANT_CPU_OR1K32BF 23 1.1 christos #define WANT_CPU 24 1.1 christos 25 1.1 christos #include "sim-main.h" 26 1.1.1.4 christos #include "sim-fpu.h" 27 1.1.1.3 christos #include "sim-signal.h" 28 1.1 christos #include "cgen-ops.h" 29 1.1 christos 30 1.1 christos /* Implement the sim invalid instruction function. This will set the error 31 1.1 christos effective address to that of the invalid instruction then call the 32 1.1 christos exception handler. */ 33 1.1 christos 34 1.1 christos SEM_PC 35 1.1 christos sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc) 36 1.1 christos { 37 1.1 christos SET_H_SYS_EEAR0 (cia); 38 1.1 christos 39 1.1 christos #ifdef WANT_CPU_OR1K32BF 40 1.1 christos or1k32bf_exception (current_cpu, cia, EXCEPT_ILLEGAL); 41 1.1 christos #endif 42 1.1 christos 43 1.1 christos return vpc; 44 1.1 christos } 45 1.1 christos 46 1.1 christos /* Generate the appropriate OpenRISC fpu exception based on the status code from 47 1.1 christos the sim fpu. */ 48 1.1 christos void 49 1.1 christos or1k32bf_fpu_error (CGEN_FPU* fpu, int status) 50 1.1 christos { 51 1.1 christos SIM_CPU *current_cpu = (SIM_CPU *)fpu->owner; 52 1.1 christos 53 1.1 christos /* If floating point exceptions are enabled. */ 54 1.1 christos if (GET_H_SYS_FPCSR_FPEE() != 0) 55 1.1 christos { 56 1.1 christos /* Set all of the status flag bits. */ 57 1.1 christos if (status 58 1.1 christos & (sim_fpu_status_invalid_snan 59 1.1 christos | sim_fpu_status_invalid_qnan 60 1.1 christos | sim_fpu_status_invalid_isi 61 1.1 christos | sim_fpu_status_invalid_idi 62 1.1 christos | sim_fpu_status_invalid_zdz 63 1.1 christos | sim_fpu_status_invalid_imz 64 1.1 christos | sim_fpu_status_invalid_cvi 65 1.1 christos | sim_fpu_status_invalid_cmp 66 1.1 christos | sim_fpu_status_invalid_sqrt)) 67 1.1 christos SET_H_SYS_FPCSR_IVF (1); 68 1.1 christos 69 1.1 christos if (status & sim_fpu_status_invalid_snan) 70 1.1 christos SET_H_SYS_FPCSR_SNF (1); 71 1.1 christos 72 1.1 christos if (status & sim_fpu_status_invalid_qnan) 73 1.1 christos SET_H_SYS_FPCSR_QNF (1); 74 1.1 christos 75 1.1 christos if (status & sim_fpu_status_overflow) 76 1.1 christos SET_H_SYS_FPCSR_OVF (1); 77 1.1 christos 78 1.1 christos if (status & sim_fpu_status_underflow) 79 1.1 christos SET_H_SYS_FPCSR_UNF (1); 80 1.1 christos 81 1.1 christos if (status 82 1.1 christos & (sim_fpu_status_invalid_isi 83 1.1 christos | sim_fpu_status_invalid_idi)) 84 1.1 christos SET_H_SYS_FPCSR_INF (1); 85 1.1 christos 86 1.1 christos if (status & sim_fpu_status_invalid_div0) 87 1.1 christos SET_H_SYS_FPCSR_DZF (1); 88 1.1 christos 89 1.1 christos if (status & sim_fpu_status_inexact) 90 1.1 christos SET_H_SYS_FPCSR_IXF (1); 91 1.1 christos 92 1.1 christos /* If any of the exception bits were actually set. */ 93 1.1 christos if (GET_H_SYS_FPCSR() 94 1.1 christos & (SPR_FIELD_MASK_SYS_FPCSR_IVF 95 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_SNF 96 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_QNF 97 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_OVF 98 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_UNF 99 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_INF 100 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_DZF 101 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_IXF)) 102 1.1 christos { 103 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu); 104 1.1 christos 105 1.1 christos /* If the sim is running in fast mode, i.e. not profiling, 106 1.1 christos per-instruction callbacks are not triggered which would allow 107 1.1 christos us to track the PC. This means we cannot track which 108 1.1 christos instruction caused the FPU error. */ 109 1.1.1.3 christos if (!PROFILE_ANY_P (current_cpu) && !TRACE_ANY_P (current_cpu)) 110 1.1 christos sim_io_eprintf 111 1.1 christos (sd, "WARNING: ignoring fpu error caught in fast mode.\n"); 112 1.1 christos else 113 1.1 christos or1k32bf_exception (current_cpu, GET_H_SYS_PPC (), EXCEPT_FPE); 114 1.1 christos } 115 1.1 christos } 116 1.1 christos } 117 1.1 christos 118 1.1 christos 119 1.1 christos /* Implement the OpenRISC exception function. This is mostly used by the 120 1.1 christos CGEN generated files. For example, this is used when handling a 121 1.1 christos overflow exception during a multiplication instruction. */ 122 1.1 christos 123 1.1 christos void 124 1.1 christos or1k32bf_exception (sim_cpu *current_cpu, USI pc, USI exnum) 125 1.1 christos { 126 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu); 127 1.1.1.4 christos struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 128 1.1 christos 129 1.1 christos if (exnum == EXCEPT_TRAP) 130 1.1 christos { 131 1.1 christos /* Trap, used for breakpoints, sends control back to gdb breakpoint 132 1.1 christos handling. */ 133 1.1 christos sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP); 134 1.1 christos } 135 1.1 christos else 136 1.1 christos { 137 1.1.1.3 christos IADDR handler_pc; 138 1.1 christos 139 1.1 christos /* Calculate the exception program counter. */ 140 1.1 christos switch (exnum) 141 1.1 christos { 142 1.1 christos case EXCEPT_RESET: 143 1.1 christos break; 144 1.1 christos 145 1.1 christos case EXCEPT_FPE: 146 1.1 christos case EXCEPT_SYSCALL: 147 1.1.1.4 christos SET_H_SYS_EPCR0 (pc + 4 - (or1k_cpu->delay_slot ? 4 : 0)); 148 1.1 christos break; 149 1.1 christos 150 1.1 christos case EXCEPT_BUSERR: 151 1.1 christos case EXCEPT_ALIGN: 152 1.1 christos case EXCEPT_ILLEGAL: 153 1.1 christos case EXCEPT_RANGE: 154 1.1.1.4 christos SET_H_SYS_EPCR0 (pc - (or1k_cpu->delay_slot ? 4 : 0)); 155 1.1 christos break; 156 1.1 christos 157 1.1 christos default: 158 1.1 christos sim_io_error (sd, "unexpected exception 0x%x raised at PC 0x%08x", 159 1.1 christos exnum, pc); 160 1.1 christos break; 161 1.1 christos } 162 1.1 christos 163 1.1 christos /* Store the current SR into ESR0. */ 164 1.1 christos SET_H_SYS_ESR0 (GET_H_SYS_SR ()); 165 1.1 christos 166 1.1 christos /* Indicate in SR if the failed instruction is in delay slot or not. */ 167 1.1.1.4 christos SET_H_SYS_SR_DSX (or1k_cpu->delay_slot); 168 1.1 christos 169 1.1.1.4 christos or1k_cpu->next_delay_slot = 0; 170 1.1 christos 171 1.1 christos /* Jump program counter into handler. */ 172 1.1.1.3 christos handler_pc = 173 1.1.1.3 christos (GET_H_SYS_SR_EPH () ? 0xf0000000 : 0x00000000) + (exnum << 8); 174 1.1 christos 175 1.1 christos sim_engine_restart (sd, current_cpu, NULL, handler_pc); 176 1.1 christos } 177 1.1 christos } 178 1.1 christos 179 1.1 christos /* Implement the return from exception instruction. This is used to return 180 1.1 christos the CPU to its previous state from within an exception handler. */ 181 1.1 christos 182 1.1 christos void 183 1.1 christos or1k32bf_rfe (sim_cpu *current_cpu) 184 1.1 christos { 185 1.1.1.4 christos struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 186 1.1.1.4 christos 187 1.1 christos SET_H_SYS_SR (GET_H_SYS_ESR0 ()); 188 1.1 christos SET_H_SYS_SR_FO (1); 189 1.1 christos 190 1.1.1.4 christos or1k_cpu->next_delay_slot = 0; 191 1.1 christos 192 1.1 christos sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL, 193 1.1 christos GET_H_SYS_EPCR0 ()); 194 1.1 christos } 195 1.1 christos 196 1.1 christos /* Implement the move from SPR instruction. This is used to read from the 197 1.1 christos CPU's special purpose registers. */ 198 1.1 christos 199 1.1 christos USI 200 1.1 christos or1k32bf_mfspr (sim_cpu *current_cpu, USI addr) 201 1.1 christos { 202 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu); 203 1.1.1.3 christos SI val; 204 1.1 christos 205 1.1 christos if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ()) 206 1.1 christos { 207 1.1 christos sim_io_eprintf (sd, "WARNING: l.mfspr in user mode (SR 0x%x)\n", 208 1.1 christos GET_H_SYS_SR ()); 209 1.1 christos return 0; 210 1.1 christos } 211 1.1 christos 212 1.1 christos if (addr >= NUM_SPR) 213 1.1 christos goto bad_address; 214 1.1 christos 215 1.1.1.3 christos val = GET_H_SPR (addr); 216 1.1 christos 217 1.1 christos switch (addr) 218 1.1 christos { 219 1.1 christos 220 1.1 christos case SPR_ADDR (SYS, VR): 221 1.1 christos case SPR_ADDR (SYS, UPR): 222 1.1 christos case SPR_ADDR (SYS, CPUCFGR): 223 1.1 christos case SPR_ADDR (SYS, SR): 224 1.1 christos case SPR_ADDR (SYS, PPC): 225 1.1 christos case SPR_ADDR (SYS, FPCSR): 226 1.1 christos case SPR_ADDR (SYS, EPCR0): 227 1.1 christos case SPR_ADDR (MAC, MACLO): 228 1.1 christos case SPR_ADDR (MAC, MACHI): 229 1.1 christos break; 230 1.1 christos 231 1.1 christos default: 232 1.1 christos if (addr < SPR_ADDR (SYS, GPR0) || addr > SPR_ADDR (SYS, GPR511)) 233 1.1 christos goto bad_address; 234 1.1 christos break; 235 1.1 christos 236 1.1 christos } 237 1.1 christos 238 1.1 christos return val; 239 1.1 christos 240 1.1 christos bad_address: 241 1.1 christos sim_io_eprintf (sd, "WARNING: l.mfspr with invalid SPR address 0x%x\n", addr); 242 1.1 christos return 0; 243 1.1 christos 244 1.1 christos } 245 1.1 christos 246 1.1 christos /* Implement the move to SPR instruction. This is used to write too the 247 1.1 christos CPU's special purpose registers. */ 248 1.1 christos 249 1.1 christos void 250 1.1 christos or1k32bf_mtspr (sim_cpu *current_cpu, USI addr, USI val) 251 1.1 christos { 252 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu); 253 1.1.1.4 christos struct or1k_sim_cpu *or1k_cpu = OR1K_SIM_CPU (current_cpu); 254 1.1 christos 255 1.1 christos if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ()) 256 1.1 christos { 257 1.1 christos sim_io_eprintf 258 1.1 christos (sd, "WARNING: l.mtspr with address 0x%x in user mode (SR 0x%x)\n", 259 1.1 christos addr, GET_H_SYS_SR ()); 260 1.1 christos return; 261 1.1 christos } 262 1.1 christos 263 1.1 christos if (addr >= NUM_SPR) 264 1.1 christos goto bad_address; 265 1.1 christos 266 1.1 christos switch (addr) 267 1.1 christos { 268 1.1 christos 269 1.1 christos case SPR_ADDR (SYS, FPCSR): 270 1.1 christos case SPR_ADDR (SYS, EPCR0): 271 1.1 christos case SPR_ADDR (SYS, ESR0): 272 1.1 christos case SPR_ADDR (MAC, MACHI): 273 1.1 christos case SPR_ADDR (MAC, MACLO): 274 1.1 christos SET_H_SPR (addr, val); 275 1.1 christos break; 276 1.1 christos 277 1.1 christos case SPR_ADDR (SYS, SR): 278 1.1 christos SET_H_SPR (addr, val); 279 1.1 christos SET_H_SYS_SR_FO (1); 280 1.1 christos break; 281 1.1 christos 282 1.1 christos case SPR_ADDR (SYS, NPC): 283 1.1.1.4 christos or1k_cpu->next_delay_slot = 0; 284 1.1 christos 285 1.1.1.4 christos sim_engine_restart (sd, current_cpu, NULL, val); 286 1.1 christos break; 287 1.1 christos 288 1.1 christos case SPR_ADDR (TICK, TTMR): 289 1.1 christos /* Allow some registers to be silently cleared. */ 290 1.1 christos if (val != 0) 291 1.1 christos sim_io_eprintf 292 1.1 christos (sd, "WARNING: l.mtspr to SPR address 0x%x with invalid value 0x%x\n", 293 1.1 christos addr, val); 294 1.1 christos break; 295 1.1 christos 296 1.1 christos default: 297 1.1 christos if (addr >= SPR_ADDR (SYS, GPR0) && addr <= SPR_ADDR (SYS, GPR511)) 298 1.1 christos SET_H_SPR (addr, val); 299 1.1 christos else 300 1.1 christos goto bad_address; 301 1.1 christos break; 302 1.1 christos 303 1.1 christos } 304 1.1 christos 305 1.1 christos return; 306 1.1 christos 307 1.1 christos bad_address: 308 1.1 christos sim_io_eprintf (sd, "WARNING: l.mtspr with invalid SPR address 0x%x\n", addr); 309 1.1 christos 310 1.1 christos } 311