traps.c revision 1.1.1.4 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