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